Skip to content

Commit

Permalink
feat(templates): improve Boilerplate SignalR PubSub integration #9526 (
Browse files Browse the repository at this point in the history
  • Loading branch information
ysmoradi authored Dec 23, 2024
1 parent 536daca commit c6ec565
Show file tree
Hide file tree
Showing 12 changed files with 65 additions and 17 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/bit.full.ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:

- name: Simple tests (no --advancedTests)
id: simple-test
continue-on-error: true
run: |
dotnet new bit-bp --name SimpleTest --database Sqlite --framework net8.0
cd SimpleTest/src/Server/SimpleTest.Server.Api/
Expand All @@ -79,6 +80,7 @@ jobs:

- name: Test Sqlite database option
id: sqlite-test
continue-on-error: true
run: |
dotnet new bit-bp --name TestSqlite --database Sqlite --advancedTests --framework net9.0
cd TestSqlite/src/Server/TestSqlite.Server.Api/
Expand All @@ -101,6 +103,7 @@ jobs:

- name: Test SqlServer database option
id: sqlserver-test
continue-on-error: true
run: |
dotnet new bit-bp --name TestSqlServer --database SqlServer --advancedTests --framework net8.0
cd TestSqlServer/src/Server/TestSqlServer.Server.Api/
Expand All @@ -121,6 +124,7 @@ jobs:

- name: Test Multilingual disabled option
id: multilingual-disabled-test
continue-on-error: true
run: |
dotnet new bit-bp --name MultilingualDisabled --database Sqlite --advancedTests --framework net8.0
cd MultilingualDisabled/src/Server/MultilingualDisabled.Server.Api/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,10 @@ private void SubscribeToSignalREventsMessages()
// You can also leverage IPubSubService to notify other components in the application.
}));

signalROnDisposables.Add(hubConnection.On<string>(SignalREvents.PUBLISH_MESSAGE, async (message) =>
signalROnDisposables.Add(hubConnection.On<string, object?>(SignalREvents.PUBLISH_MESSAGE, async (message, payload) =>
{
logger.LogInformation("SignalR Message {Message} received from server to publish.", message);
PubSubService.Publish(message);
PubSubService.Publish(message, payload);
}));

hubConnection.Closed += HubConnectionStateChange;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ protected override async Task OnInitAsync()
{
unsubscribePageTitleChanged = PubSubService.Subscribe(ClientPubSubMessages.PAGE_TITLE_CHANGED, async payload =>
{
(pageTitle, pageSubtitle) = (ValueTuple<string?, string?>)payload!;
(pageTitle, pageSubtitle) = ((string, string))payload!;

StateHasChanged();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ protected override Task OnInitAsync()
{
unsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SHOW_SNACK, async args =>
{
var (title, body, color) = (ValueTuple<string, string, BitColor>)args!;
var (title, body, color) = ((string, string, BitColor))args!;

await snackbarRef.Show(title, body, color);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ protected override async Task OnInitAsync()
{
if (payload is null) return;

user = (UserDto)payload;
user = payload is JsonElement jsonDocument
? jsonDocument.Deserialize(JsonSerializerOptions.GetTypeInfo<UserDto>())! // PROFILE_UPDATED can be invoked from server through SignalR
: (UserDto)payload;

await InvokeAsync(StateHasChanged);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public partial class DashboardPage
protected async override Task OnInitAsync()
{
//#if (signalR == true)
unsubscribe = PubSubService.Subscribe(SharedPubSubMessages.DASHBOARD_DATA_CHANGED, async _ =>
unsubscribe = PubSubService.Subscribe(ClientPubSubMessages.DASHBOARD_DATA_CHANGED, async _ =>
{
NavigationManager.NavigateTo(Urls.DashboardPage, replace: true);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Boilerplate.Client.Core.Services;

public static partial class ClientPubSubMessages
public partial class ClientPubSubMessages : SharedPubSubMessages
{
public const string SHOW_SNACK = nameof(SHOW_SNACK);
public const string SHOW_MODAL = nameof(SHOW_MODAL);
Expand All @@ -12,7 +12,6 @@ public static partial class ClientPubSubMessages
public const string THEME_CHANGED = nameof(THEME_CHANGED);
public const string OPEN_NAV_PANEL = nameof(OPEN_NAV_PANEL);
public const string CULTURE_CHANGED = nameof(CULTURE_CHANGED);
public const string PROFILE_UPDATED = nameof(PROFILE_UPDATED);
/// <summary>
/// <inheritdoc cref="Parameters.IsOnline"/>
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
using Boilerplate.Server.Api.Models.Identity;
using FluentStorage.Blobs;
//+:cnd:noEmit
using ImageMagick;
using FluentStorage.Blobs;
//#if (signalR == true)
using Microsoft.AspNetCore.SignalR;
using Boilerplate.Server.Api.SignalR;
//#endif
using Boilerplate.Shared.Dtos.Identity;
using Boilerplate.Server.Api.Models.Identity;

namespace Boilerplate.Server.Api.Controllers;

[Route("api/[controller]/[action]")]
[ApiController]
public partial class AttachmentController : AppControllerBase
{
[AutoInject] private UserManager<User> userManager = default!;

[AutoInject] private IBlobStorage blobStorage = default!;
[AutoInject] private UserManager<User> userManager = default!;
//#if (signalR == true)
[AutoInject] private IHubContext<AppHub> appHubContext = default!;
//#endif

[HttpPost]
[RequestSizeLimit(11 * 1024 * 1024 /*11MB*/)]
Expand Down Expand Up @@ -63,6 +71,10 @@ public async Task UploadProfileImage(IFormFile? file, CancellationToken cancella

throw;
}

//#if (signalR == true)
await PublishUserProfileUpdated(user.Map(), cancellationToken);
//#endif
}

[HttpDelete]
Expand All @@ -87,6 +99,10 @@ public async Task RemoveProfileImage(CancellationToken cancellationToken)
throw new ResourceValidationException(result.Errors.Select(err => new LocalizedString(err.Code, err.Description)).ToArray());

await blobStorage.DeleteAsync(filePath, cancellationToken);

//#if (signalR == true)
await PublishUserProfileUpdated(user.Map(), cancellationToken);
//#endif
}

[AllowAnonymous]
Expand All @@ -106,4 +122,17 @@ public async Task<IActionResult> GetProfileImage(Guid userId, CancellationToken

return File(await blobStorage.OpenReadAsync(filePath, cancellationToken), "image/webp", enableRangeProcessing: true);
}

//#if (signalR == true)
private async Task PublishUserProfileUpdated(UserDto user, CancellationToken cancellationToken)
{
// Notify other sessions of the user that user's info has been updated, so they'll update their UI.
var currentUserSessionId = User.GetSessionId();
var userSessionIdsExceptCurrentUserSessionId = await DbContext.UserSessions
.Where(us => us.UserId == user.Id && us.Id != currentUserSessionId && us.SignalRConnectionId != null)
.Select(us => us.SignalRConnectionId!)
.ToArrayAsync(cancellationToken);
await appHubContext.Clients.Clients(userSessionIdsExceptCurrentUserSessionId).SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.PROFILE_UPDATED, user, cancellationToken);
}
//#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private async Task PublishDashboardDataChanged(CancellationToken cancellationTok
{
// Checkout AppHub's comments for more info.
// In order to exclude current user session, gets its signalR connection id from database and use GroupExcept instead.
await appHubContext.Clients.Group("AuthenticatedClients").SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.DASHBOARD_DATA_CHANGED, cancellationToken);
await appHubContext.Clients.Group("AuthenticatedClients").SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.DASHBOARD_DATA_CHANGED, null, cancellationToken);
}
//#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public async Task RevokeSession(Guid id, CancellationToken cancellationToken)
// Checkout AppHub's comments for more info.
if (userSession.SignalRConnectionId is not null)
{
await appHubContext.Clients.Client(userSession.SignalRConnectionId).SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.SESSION_REVOKED, cancellationToken);
await appHubContext.Clients.Client(userSession.SignalRConnectionId).SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.SESSION_REVOKED, null, cancellationToken);
}
//#endif
}
Expand All @@ -107,7 +107,19 @@ public async Task<UserDto> Update(EditUserDto userDto, CancellationToken cancell
if (result.Succeeded is false)
throw new ResourceValidationException(result.Errors.Select(err => new LocalizedString(err.Code, err.Description)).ToArray());

return await GetCurrentUser(cancellationToken);
var updatedUser = await GetCurrentUser(cancellationToken);

//#if (signalR == true)
// Notify other sessions of the user that user's info has been updated, so they'll update their UI.
var currentUserSessionId = User.GetSessionId();
var userSessionIdsExceptCurrentUserSessionId = await DbContext.UserSessions
.Where(us => us.UserId == user.Id && us.Id != currentUserSessionId && us.SignalRConnectionId != null)
.Select(us => us.SignalRConnectionId!)
.ToArrayAsync(cancellationToken);
await appHubContext.Clients.Clients(userSessionIdsExceptCurrentUserSessionId).SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.PROFILE_UPDATED, updatedUser, cancellationToken);
//#endif

return updatedUser;
}

[HttpPost]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private async Task PublishDashboardDataChanged(CancellationToken cancellationTok
{
// Checkout AppHub's comments for more info.
// In order to exclude current user session, gets its signalR connection id from database and use GroupExcept instead.
await appHubContext.Clients.Group("AuthenticatedClients").SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.DASHBOARD_DATA_CHANGED, cancellationToken);
await appHubContext.Clients.Group("AuthenticatedClients").SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.DASHBOARD_DATA_CHANGED, null, cancellationToken);
}
//#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ namespace Boilerplate.Shared.Services;
/// message keys used for pub/sub messaging between server and client through SignalR.
/// For client-only pub/sub messages, refer to the ClientPubSubMessages class in the Client/Core project.
/// </summary>
public static partial class SharedPubSubMessages
public partial class SharedPubSubMessages
{
//#if (sample == "Admin")
public const string DASHBOARD_DATA_CHANGED = nameof(DASHBOARD_DATA_CHANGED);
//#endif

public const string SESSION_REVOKED = nameof(SESSION_REVOKED);

public const string PROFILE_UPDATED = nameof(PROFILE_UPDATED);
}

public static partial class SignalREvents
Expand Down

0 comments on commit c6ec565

Please sign in to comment.