From a02954838b08c421c7acc2cc6d2d9755977cb79d Mon Sep 17 00:00:00 2001 From: Ben Stein <115497763+sei-bstein@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:52:26 -0400 Subject: [PATCH] v3.22.1 (#507) * Fixed an issue that caused user roles and login info to fail to appear on the user list page in admin. * Fixed an issue that could cause Gameboard to fail to align challenge end time with engine expiration. * WIP * Minor cleanup * Fixed a bug that could cause incorrect resolution of user team membership --- .../Tests/Features/Player/TeamServiceTests.cs | 1 - .../Challenge/Services/ChallengeService.cs | 32 +++++--- .../Services/ChallengeSyncService.cs | 3 +- .../GameEngine/Services/GameEngineService.cs | 6 +- .../Features/Teams/Services/TeamService.cs | 82 ++++++------------- src/Gameboard.Api/Features/User/User.cs | 18 ++-- .../Features/User/UserService.cs | 4 + 7 files changed, 62 insertions(+), 84 deletions(-) diff --git a/src/Gameboard.Api.Tests.Unit/Tests/Features/Player/TeamServiceTests.cs b/src/Gameboard.Api.Tests.Unit/Tests/Features/Player/TeamServiceTests.cs index 3211121f..15dd8245 100644 --- a/src/Gameboard.Api.Tests.Unit/Tests/Features/Player/TeamServiceTests.cs +++ b/src/Gameboard.Api.Tests.Unit/Tests/Features/Player/TeamServiceTests.cs @@ -18,7 +18,6 @@ public void ResolveCaptain_WhenMultiplePlayersFromSameTeam_ResolvesExpected() var sut = new TeamService ( A.Fake(), - A.Fake(), A.Fake(), A.Fake(), A.Fake(), diff --git a/src/Gameboard.Api/Features/Challenge/Services/ChallengeService.cs b/src/Gameboard.Api/Features/Challenge/Services/ChallengeService.cs index 3ac555cc..68cff25e 100644 --- a/src/Gameboard.Api/Features/Challenge/Services/ChallengeService.cs +++ b/src/Gameboard.Api/Features/Challenge/Services/ChallengeService.cs @@ -14,16 +14,17 @@ using Gameboard.Api.Features.GameEngine; using Gameboard.Api.Features.Teams; using Gameboard.Api.Features.Scores; +using Gameboard.Api.Features.Users; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using ServiceStack; -using Gameboard.Api.Features.Users; namespace Gameboard.Api.Services; -public partial class ChallengeService( +public partial class ChallengeService +( IActingUserService actingUserService, ConsoleActorMap actorMap, CoreOptions coreOptions, @@ -42,7 +43,7 @@ public partial class ChallengeService( IUserRolePermissionsService permissionsService, IStore store, ITeamService teamService - ) : _Service(logger, mapper, coreOptions) +) : _Service(logger, mapper, coreOptions) { private readonly IActingUserService _actingUserService = actingUserService; private readonly ConsoleActorMap _actorMap = actorMap; @@ -177,16 +178,16 @@ public async Task Delete(string id) await _gameEngine.DeleteGamespace(entity); } - public async Task UserIsPlayingChallenge(string id, string subjectId) + public async Task UserIsPlayingChallenge(string challengeId, string userId) { var challengeTeamId = await _store .WithNoTracking() - .Where(c => c.Id == id) + .Where(c => c.Id == challengeId) .Select(c => c.TeamId) .Distinct() .SingleOrDefaultAsync(); - var userTeamIds = await _teamService.GetUserTeamIds(subjectId); + var userTeamIds = await _teamService.GetUserTeamIds(userId); return userTeamIds.Any(tId => tId == challengeTeamId); } @@ -735,14 +736,19 @@ int variant challenge.StartTime = state.StartTime; challenge.LastSyncTime = _now.Get(); - // even if a specific end time isn't set, use the expiration time instead - if (state.EndTime.IsNotEmpty()) - { - challenge.EndTime = state.EndTime; - } - else if (state.ExpirationTime.IsNotEmpty()) + // if we haven't already resolved the endtime + if (challenge.EndTime.IsEmpty()) { - challenge.EndTime = state.ExpirationTime; + // prefer the state's end time + if (state.EndTime.IsNotEmpty()) + { + challenge.EndTime = state.EndTime; + } + // but fall back on the expiration time + else if (state.ExpirationTime.IsNotEmpty()) + { + challenge.EndTime = state.ExpirationTime; + } } challenge.Events.Add(new ChallengeEvent diff --git a/src/Gameboard.Api/Features/Challenge/Services/ChallengeSyncService.cs b/src/Gameboard.Api/Features/Challenge/Services/ChallengeSyncService.cs index e15972dc..e9729b5d 100644 --- a/src/Gameboard.Api/Features/Challenge/Services/ChallengeSyncService.cs +++ b/src/Gameboard.Api/Features/Challenge/Services/ChallengeSyncService.cs @@ -126,7 +126,8 @@ await _store ( up => up .SetProperty(c => c.LastSyncTime, now) - .SetProperty(c => c.EndTime, c => playerSessionEnds.ContainsKey(challenge.PlayerId) ? playerSessionEnds[challenge.PlayerId] : c.EndTime) + .SetProperty(c => c.EndTime, c => playerSessionEnds.ContainsKey(challenge.PlayerId) ? playerSessionEnds[challenge.PlayerId] : c.EndTime), + cancellationToken ); } catch (Exception ex) diff --git a/src/Gameboard.Api/Features/GameEngine/Services/GameEngineService.cs b/src/Gameboard.Api/Features/GameEngine/Services/GameEngineService.cs index 396a5d01..4c9cf217 100644 --- a/src/Gameboard.Api/Features/GameEngine/Services/GameEngineService.cs +++ b/src/Gameboard.Api/Features/GameEngine/Services/GameEngineService.cs @@ -188,14 +188,12 @@ public async Task GetConsole(Data.Challenge entity, ConsoleReque } public IEnumerable GetGamespaceVms(GameEngineGameState state) - { - return state.Vms.Select(vm => new GameEngineGamespaceVm + => state.Vms.Select(vm => new GameEngineGamespaceVm { Id = vm.Id, Name = vm.Name, Url = _vmUrlResolver.ResolveUrl(vm) - }); - } + }).ToArray(); public async Task> AuditChallenge(Data.Challenge entity) { diff --git a/src/Gameboard.Api/Features/Teams/Services/TeamService.cs b/src/Gameboard.Api/Features/Teams/Services/TeamService.cs index c4a02310..df3559f4 100644 --- a/src/Gameboard.Api/Features/Teams/Services/TeamService.cs +++ b/src/Gameboard.Api/Features/Teams/Services/TeamService.cs @@ -42,44 +42,26 @@ public interface ITeamService Task SetSessionWindow(IEnumerable teamIds, CalculatedSessionWindow sessionWindow, CancellationToken cancellationToken); } -internal class TeamService : ITeamService, INotificationHandler +internal class TeamService +( + IActingUserService actingUserService, + IGameEngineService gameEngine, + IMapper mapper, + IMediator mediator, + INowService now, + IInternalHubBus teamHubService, + IPracticeService practiceService, + IStore store + ) : ITeamService { - private readonly IActingUserService _actingUserService; - private readonly ICacheService _cacheService; - private readonly IGameEngineService _gameEngine; - private readonly IMapper _mapper; - private readonly IMediator _mediator; - private readonly INowService _now; - private readonly IInternalHubBus _hubBus; - private readonly IPracticeService _practiceService; - private readonly IStore _store; - - public TeamService - ( - IActingUserService actingUserService, - ICacheService cacheService, - IGameEngineService gameEngine, - IMapper mapper, - IMediator mediator, - INowService now, - IInternalHubBus teamHubService, - IPracticeService practiceService, - IStore store - ) - { - _actingUserService = actingUserService; - _cacheService = cacheService; - _gameEngine = gameEngine; - _mapper = mapper; - _mediator = mediator; - _now = now; - _practiceService = practiceService; - _store = store; - _hubBus = teamHubService; - } - - public Task Handle(UserJoinedTeamNotification notification, CancellationToken cancellationToken) - => Task.Run(() => _cacheService.Invalidate(GetUserTeamIdsCacheKey(notification.UserId)), cancellationToken); + private readonly IActingUserService _actingUserService = actingUserService; + private readonly IGameEngineService _gameEngine = gameEngine; + private readonly IMapper _mapper = mapper; + private readonly IMediator _mediator = mediator; + private readonly INowService _now = now; + private readonly IInternalHubBus _hubBus = teamHubService; + private readonly IPracticeService _practiceService = practiceService; + private readonly IStore _store = store; public async Task> AddPlayers(string teamId, CancellationToken cancellationToken, params string[] playerIds) { @@ -399,22 +381,13 @@ public async Task> GetTeams(IEnumerable ids) } public Task GetUserTeamIds(string userId) - => _cacheService.GetOrCreateAsync - ( - GetUserTeamIdsCacheKey(userId), - async entry => - { - var userTeamIds = await _store - .WithNoTracking() - .Where(p => p.UserId == userId) - .Select(p => p.TeamId) - .Distinct() - .ToArrayAsync(); - - entry.Value = userTeamIds; - return userTeamIds; - } - ); + => _store + .WithNoTracking() + .Where(p => p.UserId == userId) + .Select(p => p.TeamId) + .Distinct() + .ToArrayAsync(); + public async Task IsAtGamespaceLimit(string teamId, Data.Game game, CancellationToken cancellationToken) { @@ -583,7 +556,4 @@ private async Task GetTeamState(string teamId, SimpleEntity actor, Ca Actor = actor }; } - - private string GetUserTeamIdsCacheKey(string userId) - => $"UserTeamIds:{userId}"; } diff --git a/src/Gameboard.Api/Features/User/User.cs b/src/Gameboard.Api/Features/User/User.cs index 2c5408cf..a160d4b4 100644 --- a/src/Gameboard.Api/Features/User/User.cs +++ b/src/Gameboard.Api/Features/User/User.cs @@ -81,15 +81,15 @@ public class UserSettings public class UserOnly { - public string Id { get; set; } - public string Name { get; set; } - public string NameStatus { get; set; } - public string ApprovedName { get; set; } - public SponsorWithParentSponsor Sponsor { get; set; } - public DateTimeOffset CreatedOn { get; set; } - public DateTimeOffset? LastLoginDate { get; set; } - public int LoginCount { get; set; } - public UserRoleKey Role { get; set; } + public required string Id { get; set; } + public required string Name { get; set; } + public required string NameStatus { get; set; } + public required string ApprovedName { get; set; } + public required SponsorWithParentSponsor Sponsor { get; set; } + public required DateTimeOffset CreatedOn { get; set; } + public required DateTimeOffset? LastLoginDate { get; set; } + public required int LoginCount { get; set; } + public required UserRoleKey Role { get; set; } } public class TryCreateUserResult diff --git a/src/Gameboard.Api/Features/User/UserService.cs b/src/Gameboard.Api/Features/User/UserService.cs index 41513eda..4ac3cc6f 100644 --- a/src/Gameboard.Api/Features/User/UserService.cs +++ b/src/Gameboard.Api/Features/User/UserService.cs @@ -266,6 +266,10 @@ public async Task> List(UserSearch model) where Name = u.Name, NameStatus = u.NameStatus, ApprovedName = u.ApprovedName, + CreatedOn = u.CreatedOn, + LastLoginDate = u.LastLoginDate, + LoginCount = u.LoginCount, + Role = u.Role, Sponsor = new SponsorWithParentSponsor { Id = u.SponsorId,