mirror of
https://github.com/bitwarden/server
synced 2025-12-11 05:43:35 +00:00
Notifications service unit test coverage with small refactor (#6126)
This commit is contained in:
@@ -1,34 +1,26 @@
|
|||||||
// FIXME: Update this file to be null safe and then delete the line below
|
using Azure.Storage.Queues;
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using Azure.Storage.Queues;
|
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
|
|
||||||
namespace Bit.Notifications;
|
namespace Bit.Notifications;
|
||||||
|
|
||||||
public class AzureQueueHostedService : IHostedService, IDisposable
|
public class AzureQueueHostedService : IHostedService, IDisposable
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IHubContext<NotificationsHub> _hubContext;
|
private readonly HubHelpers _hubHelpers;
|
||||||
private readonly IHubContext<AnonymousNotificationsHub> _anonymousHubContext;
|
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
private Task _executingTask;
|
private Task? _executingTask;
|
||||||
private CancellationTokenSource _cts;
|
private CancellationTokenSource? _cts;
|
||||||
private QueueClient _queueClient;
|
|
||||||
|
|
||||||
public AzureQueueHostedService(
|
public AzureQueueHostedService(
|
||||||
ILogger<AzureQueueHostedService> logger,
|
ILogger<AzureQueueHostedService> logger,
|
||||||
IHubContext<NotificationsHub> hubContext,
|
HubHelpers hubHelpers,
|
||||||
IHubContext<AnonymousNotificationsHub> anonymousHubContext,
|
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_hubContext = hubContext;
|
_hubHelpers = hubHelpers;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_anonymousHubContext = anonymousHubContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
@@ -44,32 +36,39 @@ public class AzureQueueHostedService : IHostedService, IDisposable
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogWarning("Stopping service.");
|
_logger.LogWarning("Stopping service.");
|
||||||
_cts.Cancel();
|
_cts?.Cancel();
|
||||||
await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));
|
await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ExecuteAsync(CancellationToken cancellationToken)
|
private async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_queueClient = new QueueClient(_globalSettings.Notifications.ConnectionString, "notifications");
|
var queueClient = new QueueClient(_globalSettings.Notifications.ConnectionString, "notifications");
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var messages = await _queueClient.ReceiveMessagesAsync(32);
|
var messages = await queueClient.ReceiveMessagesAsync(32, cancellationToken: cancellationToken);
|
||||||
if (messages.Value?.Any() ?? false)
|
if (messages.Value?.Any() ?? false)
|
||||||
{
|
{
|
||||||
foreach (var message in messages.Value)
|
foreach (var message in messages.Value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await HubHelpers.SendNotificationToHubAsync(
|
var decodedMessage = message.DecodeMessageText();
|
||||||
message.DecodeMessageText(), _hubContext, _anonymousHubContext, _logger, cancellationToken);
|
if (!string.IsNullOrWhiteSpace(decodedMessage))
|
||||||
await _queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
|
{
|
||||||
|
await _hubHelpers.SendNotificationToHubAsync(decodedMessage, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
await queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt,
|
||||||
|
cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -77,7 +76,8 @@ public class AzureQueueHostedService : IHostedService, IDisposable
|
|||||||
message.MessageId, message.DequeueCount);
|
message.MessageId, message.DequeueCount);
|
||||||
if (message.DequeueCount > 2)
|
if (message.DequeueCount > 2)
|
||||||
{
|
{
|
||||||
await _queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
|
await queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt,
|
||||||
|
cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,30 @@
|
|||||||
using System.Text;
|
#nullable enable
|
||||||
|
using System.Text;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
|
|
||||||
namespace Bit.Notifications;
|
namespace Bit.Notifications.Controllers;
|
||||||
|
|
||||||
[Authorize("Internal")]
|
[Authorize("Internal")]
|
||||||
public class SendController : Controller
|
public class SendController : Controller
|
||||||
{
|
{
|
||||||
private readonly IHubContext<NotificationsHub> _hubContext;
|
private readonly HubHelpers _hubHelpers;
|
||||||
private readonly IHubContext<AnonymousNotificationsHub> _anonymousHubContext;
|
|
||||||
private readonly ILogger<SendController> _logger;
|
|
||||||
|
|
||||||
public SendController(IHubContext<NotificationsHub> hubContext, IHubContext<AnonymousNotificationsHub> anonymousHubContext, ILogger<SendController> logger)
|
public SendController(HubHelpers hubHelpers)
|
||||||
{
|
{
|
||||||
_hubContext = hubContext;
|
_hubHelpers = hubHelpers;
|
||||||
_anonymousHubContext = anonymousHubContext;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("~/send")]
|
[HttpPost("~/send")]
|
||||||
[SelfHosted(SelfHostedOnly = true)]
|
[SelfHosted(SelfHostedOnly = true)]
|
||||||
public async Task PostSend()
|
public async Task PostSendAsync()
|
||||||
{
|
{
|
||||||
using (var reader = new StreamReader(Request.Body, Encoding.UTF8))
|
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
|
||||||
|
var notificationJson = await reader.ReadToEndAsync();
|
||||||
|
if (!string.IsNullOrWhiteSpace(notificationJson))
|
||||||
{
|
{
|
||||||
var notificationJson = await reader.ReadToEndAsync();
|
await _hubHelpers.SendNotificationToHubAsync(notificationJson);
|
||||||
if (!string.IsNullOrWhiteSpace(notificationJson))
|
|
||||||
{
|
|
||||||
await HubHelpers.SendNotificationToHubAsync(notificationJson, _hubContext, _anonymousHubContext, _logger);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,39 @@
|
|||||||
// FIXME: Update this file to be null safe and then delete the line below
|
using System.Text.Json;
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Text.Json;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models;
|
using Bit.Core.Models;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
namespace Bit.Notifications;
|
namespace Bit.Notifications;
|
||||||
|
|
||||||
public static class HubHelpers
|
public class HubHelpers
|
||||||
{
|
{
|
||||||
private static JsonSerializerOptions _deserializerOptions =
|
private static readonly JsonSerializerOptions _deserializerOptions = new() { PropertyNameCaseInsensitive = true };
|
||||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
|
||||||
|
|
||||||
private static readonly string _receiveMessageMethod = "ReceiveMessage";
|
private static readonly string _receiveMessageMethod = "ReceiveMessage";
|
||||||
|
|
||||||
public static async Task SendNotificationToHubAsync(
|
private readonly IHubContext<NotificationsHub> _hubContext;
|
||||||
string notificationJson,
|
private readonly IHubContext<AnonymousNotificationsHub> _anonymousHubContext;
|
||||||
IHubContext<NotificationsHub> hubContext,
|
private readonly ILogger<HubHelpers> _logger;
|
||||||
|
|
||||||
|
public HubHelpers(IHubContext<NotificationsHub> hubContext,
|
||||||
IHubContext<AnonymousNotificationsHub> anonymousHubContext,
|
IHubContext<AnonymousNotificationsHub> anonymousHubContext,
|
||||||
ILogger logger,
|
ILogger<HubHelpers> logger)
|
||||||
CancellationToken cancellationToken = default(CancellationToken)
|
{
|
||||||
)
|
_hubContext = hubContext;
|
||||||
|
_anonymousHubContext = anonymousHubContext;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendNotificationToHubAsync(string notificationJson, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var notification =
|
var notification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<object>>(notificationJson, _deserializerOptions);
|
JsonSerializer.Deserialize<PushNotificationData<object>>(notificationJson, _deserializerOptions);
|
||||||
logger.LogInformation("Sending notification: {NotificationType}", notification.Type);
|
if (notification is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Sending notification: {NotificationType}", notification.Type);
|
||||||
switch (notification.Type)
|
switch (notification.Type)
|
||||||
{
|
{
|
||||||
case PushType.SyncCipherUpdate:
|
case PushType.SyncCipherUpdate:
|
||||||
@@ -35,14 +43,19 @@ public static class HubHelpers
|
|||||||
var cipherNotification =
|
var cipherNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<SyncCipherPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<SyncCipherPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
|
if (cipherNotification is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (cipherNotification.Payload.UserId.HasValue)
|
if (cipherNotification.Payload.UserId.HasValue)
|
||||||
{
|
{
|
||||||
await hubContext.Clients.User(cipherNotification.Payload.UserId.ToString())
|
await _hubContext.Clients.User(cipherNotification.Payload.UserId.Value.ToString())
|
||||||
.SendAsync(_receiveMessageMethod, cipherNotification, cancellationToken);
|
.SendAsync(_receiveMessageMethod, cipherNotification, cancellationToken);
|
||||||
}
|
}
|
||||||
else if (cipherNotification.Payload.OrganizationId.HasValue)
|
else if (cipherNotification.Payload.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await hubContext.Clients
|
await _hubContext.Clients
|
||||||
.Group(NotificationsHub.GetOrganizationGroup(cipherNotification.Payload.OrganizationId.Value))
|
.Group(NotificationsHub.GetOrganizationGroup(cipherNotification.Payload.OrganizationId.Value))
|
||||||
.SendAsync(_receiveMessageMethod, cipherNotification, cancellationToken);
|
.SendAsync(_receiveMessageMethod, cipherNotification, cancellationToken);
|
||||||
}
|
}
|
||||||
@@ -54,7 +67,12 @@ public static class HubHelpers
|
|||||||
var folderNotification =
|
var folderNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<SyncFolderPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<SyncFolderPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
await hubContext.Clients.User(folderNotification.Payload.UserId.ToString())
|
if (folderNotification is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hubContext.Clients.User(folderNotification.Payload.UserId.ToString())
|
||||||
.SendAsync(_receiveMessageMethod, folderNotification, cancellationToken);
|
.SendAsync(_receiveMessageMethod, folderNotification, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PushType.SyncCiphers:
|
case PushType.SyncCiphers:
|
||||||
@@ -66,7 +84,12 @@ public static class HubHelpers
|
|||||||
var userNotification =
|
var userNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<LogOutPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<LogOutPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
await hubContext.Clients.User(userNotification.Payload.UserId.ToString())
|
if (userNotification is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hubContext.Clients.User(userNotification.Payload.UserId.ToString())
|
||||||
.SendAsync(_receiveMessageMethod, userNotification, cancellationToken);
|
.SendAsync(_receiveMessageMethod, userNotification, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PushType.SyncSendCreate:
|
case PushType.SyncSendCreate:
|
||||||
@@ -75,36 +98,65 @@ public static class HubHelpers
|
|||||||
var sendNotification =
|
var sendNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<SyncSendPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<SyncSendPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
await hubContext.Clients.User(sendNotification.Payload.UserId.ToString())
|
if (sendNotification is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hubContext.Clients.User(sendNotification.Payload.UserId.ToString())
|
||||||
.SendAsync(_receiveMessageMethod, sendNotification, cancellationToken);
|
.SendAsync(_receiveMessageMethod, sendNotification, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PushType.AuthRequestResponse:
|
case PushType.AuthRequestResponse:
|
||||||
var authRequestResponseNotification =
|
var authRequestResponseNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
await anonymousHubContext.Clients.Group(authRequestResponseNotification.Payload.Id.ToString())
|
if (authRequestResponseNotification is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _anonymousHubContext.Clients.Group(authRequestResponseNotification.Payload.Id.ToString())
|
||||||
.SendAsync("AuthRequestResponseRecieved", authRequestResponseNotification, cancellationToken);
|
.SendAsync("AuthRequestResponseRecieved", authRequestResponseNotification, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PushType.AuthRequest:
|
case PushType.AuthRequest:
|
||||||
var authRequestNotification =
|
var authRequestNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
await hubContext.Clients.User(authRequestNotification.Payload.UserId.ToString())
|
if (authRequestNotification is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hubContext.Clients.User(authRequestNotification.Payload.UserId.ToString())
|
||||||
.SendAsync(_receiveMessageMethod, authRequestNotification, cancellationToken);
|
.SendAsync(_receiveMessageMethod, authRequestNotification, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PushType.SyncOrganizationStatusChanged:
|
case PushType.SyncOrganizationStatusChanged:
|
||||||
var orgStatusNotification =
|
var orgStatusNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(orgStatusNotification.Payload.OrganizationId))
|
if (orgStatusNotification is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hubContext.Clients
|
||||||
|
.Group(NotificationsHub.GetOrganizationGroup(orgStatusNotification.Payload.OrganizationId))
|
||||||
.SendAsync(_receiveMessageMethod, orgStatusNotification, cancellationToken);
|
.SendAsync(_receiveMessageMethod, orgStatusNotification, cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PushType.SyncOrganizationCollectionSettingChanged:
|
case PushType.SyncOrganizationCollectionSettingChanged:
|
||||||
var organizationCollectionSettingsChangedNotification =
|
var organizationCollectionSettingsChangedNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(organizationCollectionSettingsChangedNotification.Payload.OrganizationId))
|
if (organizationCollectionSettingsChangedNotification is null)
|
||||||
.SendAsync(_receiveMessageMethod, organizationCollectionSettingsChangedNotification, cancellationToken);
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hubContext.Clients
|
||||||
|
.Group(NotificationsHub.GetOrganizationGroup(organizationCollectionSettingsChangedNotification
|
||||||
|
.Payload.OrganizationId))
|
||||||
|
.SendAsync(_receiveMessageMethod, organizationCollectionSettingsChangedNotification,
|
||||||
|
cancellationToken);
|
||||||
break;
|
break;
|
||||||
case PushType.OrganizationBankAccountVerified:
|
case PushType.OrganizationBankAccountVerified:
|
||||||
var organizationBankAccountVerifiedNotification =
|
var organizationBankAccountVerifiedNotification =
|
||||||
@@ -124,9 +176,14 @@ public static class HubHelpers
|
|||||||
case PushType.NotificationStatus:
|
case PushType.NotificationStatus:
|
||||||
var notificationData = JsonSerializer.Deserialize<PushNotificationData<NotificationPushNotification>>(
|
var notificationData = JsonSerializer.Deserialize<PushNotificationData<NotificationPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
|
if (notificationData is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (notificationData.Payload.InstallationId.HasValue)
|
if (notificationData.Payload.InstallationId.HasValue)
|
||||||
{
|
{
|
||||||
await hubContext.Clients.Group(NotificationsHub.GetInstallationGroup(
|
await _hubContext.Clients.Group(NotificationsHub.GetInstallationGroup(
|
||||||
notificationData.Payload.InstallationId.Value, notificationData.Payload.ClientType))
|
notificationData.Payload.InstallationId.Value, notificationData.Payload.ClientType))
|
||||||
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
||||||
}
|
}
|
||||||
@@ -134,27 +191,34 @@ public static class HubHelpers
|
|||||||
{
|
{
|
||||||
if (notificationData.Payload.ClientType == ClientType.All)
|
if (notificationData.Payload.ClientType == ClientType.All)
|
||||||
{
|
{
|
||||||
await hubContext.Clients.User(notificationData.Payload.UserId.ToString())
|
await _hubContext.Clients.User(notificationData.Payload.UserId.Value.ToString())
|
||||||
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await hubContext.Clients.Group(NotificationsHub.GetUserGroup(
|
await _hubContext.Clients.Group(NotificationsHub.GetUserGroup(
|
||||||
notificationData.Payload.UserId.Value, notificationData.Payload.ClientType))
|
notificationData.Payload.UserId.Value, notificationData.Payload.ClientType))
|
||||||
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (notificationData.Payload.OrganizationId.HasValue)
|
else if (notificationData.Payload.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(
|
await _hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(
|
||||||
notificationData.Payload.OrganizationId.Value, notificationData.Payload.ClientType))
|
notificationData.Payload.OrganizationId.Value, notificationData.Payload.ClientType))
|
||||||
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PushType.RefreshSecurityTasks:
|
case PushType.RefreshSecurityTasks:
|
||||||
var pendingTasksData = JsonSerializer.Deserialize<PushNotificationData<UserPushNotification>>(notificationJson, _deserializerOptions);
|
var pendingTasksData =
|
||||||
await hubContext.Clients.User(pendingTasksData.Payload.UserId.ToString())
|
JsonSerializer.Deserialize<PushNotificationData<UserPushNotification>>(notificationJson,
|
||||||
|
_deserializerOptions);
|
||||||
|
if (pendingTasksData is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hubContext.Clients.User(pendingTasksData.Payload.UserId.ToString())
|
||||||
.SendAsync(_receiveMessageMethod, pendingTasksData, cancellationToken);
|
.SendAsync(_receiveMessageMethod, pendingTasksData, cancellationToken);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public class Startup
|
|||||||
}
|
}
|
||||||
services.AddSingleton<IUserIdProvider, SubjectUserIdProvider>();
|
services.AddSingleton<IUserIdProvider, SubjectUserIdProvider>();
|
||||||
services.AddSingleton<ConnectionCounter>();
|
services.AddSingleton<ConnectionCounter>();
|
||||||
|
services.AddSingleton<HubHelpers>();
|
||||||
|
|
||||||
// Mvc
|
// Mvc
|
||||||
services.AddMvc();
|
services.AddMvc();
|
||||||
|
|||||||
250
test/Notifications.Test/HubHelpersTest.cs
Normal file
250
test/Notifications.Test/HubHelpersTest.cs
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System.Text.Json;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models;
|
||||||
|
using Bit.Core.Test.NotificationCenter.AutoFixture;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Bit.Notifications;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using NSubstitute;
|
||||||
|
|
||||||
|
namespace Notifications.Test;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
[NotificationCustomize(false)]
|
||||||
|
public class HubHelpersTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task SendNotificationToHubAsync_NotificationPushNotificationGlobal_NothingSent(
|
||||||
|
SutProvider<HubHelpers> sutProvider,
|
||||||
|
NotificationPushNotification notification,
|
||||||
|
string contextId, CancellationToken cancellationToke)
|
||||||
|
{
|
||||||
|
notification.Global = true;
|
||||||
|
notification.InstallationId = null;
|
||||||
|
notification.UserId = null;
|
||||||
|
notification.OrganizationId = null;
|
||||||
|
|
||||||
|
var json = ToNotificationJson(notification, PushType.Notification, contextId);
|
||||||
|
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToke);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).Group(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
|
||||||
|
.Group(Arg.Any<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task
|
||||||
|
SendNotificationToHubAsync_NotificationPushNotificationInstallationIdProvidedClientTypeAll_SentToGroupInstallation(
|
||||||
|
SutProvider<HubHelpers> sutProvider,
|
||||||
|
NotificationPushNotification notification,
|
||||||
|
string contextId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
notification.UserId = null;
|
||||||
|
notification.OrganizationId = null;
|
||||||
|
notification.ClientType = ClientType.All;
|
||||||
|
|
||||||
|
var json = ToNotificationJson(notification, PushType.Notification, contextId);
|
||||||
|
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
|
||||||
|
.Group($"Installation_{notification.InstallationId!.Value.ToString()}")
|
||||||
|
.Received(1)
|
||||||
|
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
|
||||||
|
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
|
||||||
|
PushType.Notification, contextId)),
|
||||||
|
cancellationToken);
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
|
||||||
|
.Group(Arg.Any<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(ClientType.Browser)]
|
||||||
|
[BitAutoData(ClientType.Desktop)]
|
||||||
|
[BitAutoData(ClientType.Mobile)]
|
||||||
|
[BitAutoData(ClientType.Web)]
|
||||||
|
public async Task
|
||||||
|
SendNotificationToHubAsync_NotificationPushNotificationInstallationIdProvidedClientTypeNotAll_SentToGroupInstallationClientType(
|
||||||
|
ClientType clientType, SutProvider<HubHelpers> sutProvider,
|
||||||
|
NotificationPushNotification notification,
|
||||||
|
string contextId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
notification.UserId = null;
|
||||||
|
notification.OrganizationId = null;
|
||||||
|
notification.ClientType = clientType;
|
||||||
|
|
||||||
|
var json = ToNotificationJson(notification, PushType.Notification, contextId);
|
||||||
|
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
|
||||||
|
.Group($"Installation_ClientType_{notification.InstallationId!.Value}_{clientType}")
|
||||||
|
.Received(1)
|
||||||
|
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
|
||||||
|
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
|
||||||
|
PushType.Notification, contextId)),
|
||||||
|
cancellationToken);
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
|
||||||
|
.Group(Arg.Any<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(false)]
|
||||||
|
[BitAutoData(true)]
|
||||||
|
public async Task SendNotificationToHubAsync_NotificationPushNotificationUserIdProvidedClientTypeAll_SentToUser(
|
||||||
|
bool organizationIdProvided, SutProvider<HubHelpers> sutProvider,
|
||||||
|
NotificationPushNotification notification,
|
||||||
|
string contextId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
notification.InstallationId = null;
|
||||||
|
notification.ClientType = ClientType.All;
|
||||||
|
if (!organizationIdProvided)
|
||||||
|
{
|
||||||
|
notification.OrganizationId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = ToNotificationJson(notification, PushType.Notification, contextId);
|
||||||
|
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
|
||||||
|
.User(notification.UserId!.Value.ToString())
|
||||||
|
.Received(1)
|
||||||
|
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
|
||||||
|
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
|
||||||
|
PushType.Notification, contextId)),
|
||||||
|
cancellationToken);
|
||||||
|
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).Group(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
|
||||||
|
.Group(Arg.Any<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(false, ClientType.Browser)]
|
||||||
|
[BitAutoData(false, ClientType.Desktop)]
|
||||||
|
[BitAutoData(false, ClientType.Mobile)]
|
||||||
|
[BitAutoData(false, ClientType.Web)]
|
||||||
|
[BitAutoData(true, ClientType.Browser)]
|
||||||
|
[BitAutoData(true, ClientType.Desktop)]
|
||||||
|
[BitAutoData(true, ClientType.Mobile)]
|
||||||
|
[BitAutoData(true, ClientType.Web)]
|
||||||
|
public async Task
|
||||||
|
SendNotificationToHubAsync_NotificationPushNotificationUserIdProvidedClientTypeNotAll_SentToGroupUserClientType(
|
||||||
|
bool organizationIdProvided, ClientType clientType, SutProvider<HubHelpers> sutProvider,
|
||||||
|
NotificationPushNotification notification,
|
||||||
|
string contextId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
notification.InstallationId = null;
|
||||||
|
notification.ClientType = clientType;
|
||||||
|
if (!organizationIdProvided)
|
||||||
|
{
|
||||||
|
notification.OrganizationId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = ToNotificationJson(notification, PushType.Notification, contextId);
|
||||||
|
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
|
||||||
|
.Group($"UserClientType_{notification.UserId!.Value}_{clientType}")
|
||||||
|
.Received(1)
|
||||||
|
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
|
||||||
|
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
|
||||||
|
PushType.Notification, contextId)),
|
||||||
|
cancellationToken);
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
|
||||||
|
.Group(Arg.Any<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task
|
||||||
|
SendNotificationToHubAsync_NotificationPushNotificationOrganizationIdProvidedClientTypeAll_SentToGroupOrganization(
|
||||||
|
SutProvider<HubHelpers> sutProvider, string contextId,
|
||||||
|
NotificationPushNotification notification,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
notification.UserId = null;
|
||||||
|
notification.InstallationId = null;
|
||||||
|
notification.ClientType = ClientType.All;
|
||||||
|
|
||||||
|
var json = ToNotificationJson(notification, PushType.Notification, contextId);
|
||||||
|
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
|
||||||
|
.Group($"Organization_{notification.OrganizationId!.Value}")
|
||||||
|
.Received(1)
|
||||||
|
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
|
||||||
|
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
|
||||||
|
PushType.Notification, contextId)),
|
||||||
|
cancellationToken);
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
|
||||||
|
.Group(Arg.Any<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(ClientType.Browser)]
|
||||||
|
[BitAutoData(ClientType.Desktop)]
|
||||||
|
[BitAutoData(ClientType.Mobile)]
|
||||||
|
[BitAutoData(ClientType.Web)]
|
||||||
|
public async Task
|
||||||
|
SendNotificationToHubAsync_NotificationPushNotificationOrganizationIdProvidedClientTypeNotAll_SentToGroupOrganizationClientType(
|
||||||
|
ClientType clientType, SutProvider<HubHelpers> sutProvider, string contextId,
|
||||||
|
NotificationPushNotification notification,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
notification.UserId = null;
|
||||||
|
notification.InstallationId = null;
|
||||||
|
notification.ClientType = clientType;
|
||||||
|
|
||||||
|
var json = ToNotificationJson(notification, PushType.Notification, contextId);
|
||||||
|
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
|
||||||
|
.Group($"OrganizationClientType_{notification.OrganizationId!.Value}_{clientType}")
|
||||||
|
.Received(1)
|
||||||
|
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
|
||||||
|
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
|
||||||
|
PushType.Notification, contextId)),
|
||||||
|
cancellationToken);
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
|
||||||
|
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
|
||||||
|
.Group(Arg.Any<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToNotificationJson(object payload, PushType type, string contextId)
|
||||||
|
{
|
||||||
|
var notification = new PushNotificationData<object>(type, payload, contextId);
|
||||||
|
return JsonSerializer.Serialize(notification, JsonHelpers.IgnoreWritingNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNotificationPushNotificationEqual(NotificationPushNotification expected, object? actual,
|
||||||
|
PushType type, string contextId)
|
||||||
|
{
|
||||||
|
if (actual is not PushNotificationData<NotificationPushNotification> pushNotificationData)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pushNotificationData.Type == type &&
|
||||||
|
pushNotificationData.ContextId == contextId &&
|
||||||
|
expected.Id == pushNotificationData.Payload.Id &&
|
||||||
|
expected.UserId == pushNotificationData.Payload.UserId &&
|
||||||
|
expected.OrganizationId == pushNotificationData.Payload.OrganizationId &&
|
||||||
|
expected.ClientType == pushNotificationData.Payload.ClientType &&
|
||||||
|
expected.RevisionDate == pushNotificationData.Payload.RevisionDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,5 +18,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Notifications\Notifications.csproj" />
|
<ProjectReference Include="..\..\src\Notifications\Notifications.csproj" />
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Core.Test\Core.Test.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user