From 3e95033fe747d254ef6c62dac6c5d86d3cbe1112 Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Wed, 11 Feb 2026 21:58:08 +0100 Subject: [PATCH 1/5] Domain rules for kicking and banning users from whiteboards --- .../Whiteboard/Validation/WhiteboardErrors.cs | 23 +++++++++++++++++++ .../Models/Whiteboard/Whiteboard.BanUser.cs | 18 +++++++++++++++ .../Models/Whiteboard/Whiteboard.KickUser.cs | 18 +++++++++++++++ .../Models/Whiteboard/Whiteboard.UnbanUser.cs | 18 +++++++++++++++ .../WhiteboardMembership.Ban.cs | 11 +++++++++ .../WhiteboardMembership.Kick.cs | 11 +++++++++ .../WhiteboardMembership.Unban.cs | 11 +++++++++ .../WhiteboardMembership.cs | 2 +- 8 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.BanUser.cs create mode 100644 dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.KickUser.cs create mode 100644 dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.UnbanUser.cs create mode 100644 dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Ban.cs create mode 100644 dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Kick.cs create mode 100644 dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Unban.cs diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardErrors.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardErrors.cs index b06bafc..262e72b 100644 --- a/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardErrors.cs +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardErrors.cs @@ -14,5 +14,28 @@ public class WhiteboardErrors : AbstractErrors return CreateValidationError(code, message); } + + public static ValidationError OnlyOwnerCanBanOtherUsers(UserId currentUserId) + { + string code = "only_owner_can_ban_other_users"; + string message = $"Only owner of whiteboard can ban other users. Current user id: '{currentUserId}' is not the owner."; + return CreateValidationError(code, message); + } + + public static ValidationError OnlyOwnerCanUnbanOtherUsers(UserId currentUserId) + { + string code = "only_owner_can_unban_other_users"; + string message = $"Only owner of whiteboard can unban other users. Current user id: '{currentUserId}' is not the owner."; + + return CreateValidationError(code, message); + } + + public static ValidationError OnlyOwnerCanKickOtherUsers(UserId currentUserId) + { + string code = "only_owner_can_unban_other_users"; + string message = $"Only owner of whiteboard can kick other users. Current user id: '{currentUserId}' is not the owner."; + + return CreateValidationError(code, message); + } } \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.BanUser.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.BanUser.cs new file mode 100644 index 0000000..57d5dfb --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.BanUser.cs @@ -0,0 +1,18 @@ +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.Validation; + +namespace AipsCore.Domain.Models.Whiteboard; + +public partial class Whiteboard +{ + public void BanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership) + { + if (WhiteboardOwnerId != currentUserId) + { + throw new ValidationException(WhiteboardErrors.OnlyOwnerCanBanOtherUsers(currentUserId)); + } + + whiteboardMembership.Ban(); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.KickUser.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.KickUser.cs new file mode 100644 index 0000000..e9d4579 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.KickUser.cs @@ -0,0 +1,18 @@ +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.Validation; + +namespace AipsCore.Domain.Models.Whiteboard; + +public partial class Whiteboard +{ + public void KickUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership) + { + if (WhiteboardOwnerId != currentUserId) + { + throw new ValidationException(WhiteboardErrors.OnlyOwnerCanKickOtherUsers(currentUserId)); + } + + whiteboardMembership.Kick(); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.UnbanUser.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.UnbanUser.cs new file mode 100644 index 0000000..dabc832 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.UnbanUser.cs @@ -0,0 +1,18 @@ +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.Validation; + +namespace AipsCore.Domain.Models.Whiteboard; + +public partial class Whiteboard +{ + public void UnbanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership) + { + if (WhiteboardOwnerId != currentUserId) + { + throw new ValidationException(WhiteboardErrors.OnlyOwnerCanUnbanOtherUsers(currentUserId)); + } + + whiteboardMembership.Unban(); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Ban.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Ban.cs new file mode 100644 index 0000000..6befd88 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Ban.cs @@ -0,0 +1,11 @@ +using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +namespace AipsCore.Domain.Models.WhiteboardMembership; + +public partial class WhiteboardMembership +{ + public void Ban() + { + IsBanned = new WhiteboardMembershipIsBanned(true); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Kick.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Kick.cs new file mode 100644 index 0000000..92acf4a --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Kick.cs @@ -0,0 +1,11 @@ +using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +namespace AipsCore.Domain.Models.WhiteboardMembership; + +public partial class WhiteboardMembership +{ + public void Kick() + { + CanJoin = new WhiteboardMembershipCanJoin(false); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Unban.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Unban.cs new file mode 100644 index 0000000..9072889 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.Unban.cs @@ -0,0 +1,11 @@ +using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +namespace AipsCore.Domain.Models.WhiteboardMembership; + +public partial class WhiteboardMembership +{ + public void Unban() + { + IsBanned = new WhiteboardMembershipIsBanned(false); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.cs index 9cc612b..bfad6da 100644 --- a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.cs +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.cs @@ -5,7 +5,7 @@ using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; namespace AipsCore.Domain.Models.WhiteboardMembership; -public class WhiteboardMembership : DomainModel +public partial class WhiteboardMembership : DomainModel { public WhiteboardId WhiteboardId { get; private set; } public UserId UserId { get; private set; } From d1bf7d3558e96b94185f700065b1fdc6786c4eb3 Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Wed, 11 Feb 2026 21:58:34 +0100 Subject: [PATCH 2/5] Commands and handlers --- .../BanUserFromWhiteboardCommand.cs | 5 ++ .../BanUserFromWhiteboardCommandErrors.cs | 18 ++++++ .../BanUserFromWhiteboardCommandHandler.cs | 55 +++++++++++++++++++ .../KickUserFromWhiteboardCommand.cs | 5 ++ .../KickUserFromWhiteboardCommandErrors.cs | 18 ++++++ .../KickUserFromWhiteboardCommandHandler.cs | 55 +++++++++++++++++++ .../UnbanUserFromWhiteboardCommand.cs | 5 ++ .../UnbanUserFromWhiteboardCommandErrors.cs | 18 ++++++ .../UnbanUserFromWhiteboardCommandHandler.cs | 55 +++++++++++++++++++ 9 files changed, 234 insertions(+) create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandErrors.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandHandler.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommand.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommandErrors.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommandHandler.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommand.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommandErrors.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommandHandler.cs diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs new file mode 100644 index 0000000..e654922 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs @@ -0,0 +1,5 @@ +using AipsCore.Application.Abstract.Command; + +namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard; + +public record BanUserFromWhiteboardCommand(string CallerId, string UserId, string WhiteboardId) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandErrors.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandErrors.cs new file mode 100644 index 0000000..b4a2f97 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandErrors.cs @@ -0,0 +1,18 @@ +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; + +namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard; + +public static class BanUserFromWhiteboardCommandErrors +{ + public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId) + => new ValidationError( + Code: "whiteboard_membership_not_found", + Message: $"User with id '{userId}' is not a member of whiteboard with id '{whiteboardId}'"); + + public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId) + => new ValidationError( + Code: "whiteboard_not_found", + Message: $"Whiteboard with id '{whiteboardId}' not found."); +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandHandler.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandHandler.cs new file mode 100644 index 0000000..efcc9a9 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandHandler.cs @@ -0,0 +1,55 @@ +using AipsCore.Application.Abstract.Command; +using AipsCore.Application.Abstract.UserContext; +using AipsCore.Domain.Abstract; +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.External; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; +using AipsCore.Domain.Models.WhiteboardMembership.External; + +namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard; + +public class BanUserFromWhiteboardCommandHandler : ICommandHandler +{ + private readonly IWhiteboardRepository _whiteboardRepository; + private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository; + private readonly IUserContext _userContext; + private readonly IUnitOfWork _unitOfWork; + + public BanUserFromWhiteboardCommandHandler( + IWhiteboardRepository whiteboardRepository, + IWhiteboardMembershipRepository whiteboardMembershipRepository, + IUserContext userContext, + IUnitOfWork unitOfWork) + { + _whiteboardMembershipRepository = whiteboardMembershipRepository; + _userContext = userContext; + _unitOfWork = unitOfWork; + _whiteboardRepository = whiteboardRepository; + } + + public async Task Handle(BanUserFromWhiteboardCommand command, CancellationToken cancellationToken = default) + { + var whiteboardId = new WhiteboardId(command.WhiteboardId); + var userId = new UserId(command.UserId); + + var whiteboard = await _whiteboardRepository.GetByIdAsync(whiteboardId, cancellationToken); + + if (whiteboard is null) + { + throw new ValidationException(BanUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId)); + } + + var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken); + + if (membership is null) + { + throw new ValidationException(BanUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId)); + } + + whiteboard.BanUser(_userContext.GetCurrentUserId(), membership); + + await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommand.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommand.cs new file mode 100644 index 0000000..647f83a --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommand.cs @@ -0,0 +1,5 @@ +using AipsCore.Application.Abstract.Command; + +namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard; + +public record KickUserFromWhiteboardCommand(string UserId, string WhiteboardId) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommandErrors.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommandErrors.cs new file mode 100644 index 0000000..dbc5489 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommandErrors.cs @@ -0,0 +1,18 @@ +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; + +namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard; + +public static class KickUserFromWhiteboardCommandErrors +{ + public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId) + => new ValidationError( + Code: "whiteboard_membership_not_found", + Message: $"User with id '{userId}' is not a member of whiteboard with id '{whiteboardId}'"); + + public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId) + => new ValidationError( + Code: "whiteboard_not_found", + Message: $"Whiteboard with id '{whiteboardId}' not found."); +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommandHandler.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommandHandler.cs new file mode 100644 index 0000000..b30b825 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/KickUserFromWhiteboard/KickUserFromWhiteboardCommandHandler.cs @@ -0,0 +1,55 @@ +using AipsCore.Application.Abstract.Command; +using AipsCore.Application.Abstract.UserContext; +using AipsCore.Domain.Abstract; +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.External; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; +using AipsCore.Domain.Models.WhiteboardMembership.External; + +namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard; + +public class KickUserFromWhiteboardCommandHandler : ICommandHandler +{ + private readonly IWhiteboardRepository _whiteboardRepository; + private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository; + private readonly IUserContext _userContext; + private readonly IUnitOfWork _unitOfWork; + + public KickUserFromWhiteboardCommandHandler( + IWhiteboardRepository whiteboardRepository, + IWhiteboardMembershipRepository whiteboardMembershipRepository, + IUserContext userContext, + IUnitOfWork unitOfWork) + { + _whiteboardMembershipRepository = whiteboardMembershipRepository; + _userContext = userContext; + _unitOfWork = unitOfWork; + _whiteboardRepository = whiteboardRepository; + } + + public async Task Handle(KickUserFromWhiteboardCommand command, CancellationToken cancellationToken = default) + { + var whiteboardId = new WhiteboardId(command.WhiteboardId); + var userId = new UserId(command.UserId); + + var whiteboard = await _whiteboardRepository.GetByIdAsync(whiteboardId, cancellationToken); + + if (whiteboard is null) + { + throw new ValidationException(KickUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId)); + } + + var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken); + + if (membership is null) + { + throw new ValidationException(KickUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId)); + } + + whiteboard.KickUser(_userContext.GetCurrentUserId(), membership); + + await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommand.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommand.cs new file mode 100644 index 0000000..f0a1be2 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommand.cs @@ -0,0 +1,5 @@ +using AipsCore.Application.Abstract.Command; + +namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard; + +public record UnbanUserFromWhiteboardCommand(string UserId, string WhiteboardId) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommandErrors.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommandErrors.cs new file mode 100644 index 0000000..f04e3d9 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommandErrors.cs @@ -0,0 +1,18 @@ +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; + +namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard; + +public static class UnbanUserFromWhiteboardCommandErrors +{ + public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId) + => new ValidationError( + Code: "whiteboard_membership_not_found", + Message: $"User with id '{userId}' is not a member of whiteboard with id '{whiteboardId}'"); + + public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId) + => new ValidationError( + Code: "whiteboard_not_found", + Message: $"Whiteboard with id '{whiteboardId}' not found."); +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommandHandler.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommandHandler.cs new file mode 100644 index 0000000..326a5b3 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/UnbanUserFromWhiteboard/UnbanUserFromWhiteboardCommandHandler.cs @@ -0,0 +1,55 @@ +using AipsCore.Application.Abstract.Command; +using AipsCore.Application.Abstract.UserContext; +using AipsCore.Domain.Abstract; +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.External; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; +using AipsCore.Domain.Models.WhiteboardMembership.External; + +namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard; + +public class UnbanUserFromWhiteboardCommandHandler : ICommandHandler +{ + private readonly IWhiteboardRepository _whiteboardRepository; + private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository; + private readonly IUserContext _userContext; + private readonly IUnitOfWork _unitOfWork; + + public UnbanUserFromWhiteboardCommandHandler( + IWhiteboardRepository whiteboardRepository, + IWhiteboardMembershipRepository whiteboardMembershipRepository, + IUserContext userContext, + IUnitOfWork unitOfWork) + { + _whiteboardRepository = whiteboardRepository; + _whiteboardMembershipRepository = whiteboardMembershipRepository; + _userContext = userContext; + _unitOfWork = unitOfWork; + } + + public async Task Handle(UnbanUserFromWhiteboardCommand command, CancellationToken cancellationToken = default) + { + var whiteboardId = new WhiteboardId(command.WhiteboardId); + var userId = new UserId(command.UserId); + + var whiteboard = await _whiteboardRepository.GetByIdAsync(whiteboardId, cancellationToken); + + if (whiteboard is null) + { + throw new ValidationException(UnbanUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId)); + } + + var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken); + + if (membership is null) + { + throw new ValidationException(UnbanUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId)); + } + + whiteboard.UnbanUser(_userContext.GetCurrentUserId(), membership); + + await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file From fcedc54446e4d7b3b3c95db31656570c52294550 Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Wed, 11 Feb 2026 21:59:08 +0100 Subject: [PATCH 3/5] Temporary solution for UserContext (callerId) --- .../Abstract/UserContext/IUserContext.cs | 8 ++++++++ .../DI/AipsRegistrationExtensions.cs | 5 ++--- .../DI/UserContextRegistrationExtension.cs | 14 ++++++++++++++ dotnet/AipsCore/Infrastructure/UserContext.cs | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 dotnet/AipsCore/Application/Abstract/UserContext/IUserContext.cs create mode 100644 dotnet/AipsCore/Infrastructure/DI/UserContextRegistrationExtension.cs create mode 100644 dotnet/AipsCore/Infrastructure/UserContext.cs diff --git a/dotnet/AipsCore/Application/Abstract/UserContext/IUserContext.cs b/dotnet/AipsCore/Application/Abstract/UserContext/IUserContext.cs new file mode 100644 index 0000000..3366347 --- /dev/null +++ b/dotnet/AipsCore/Application/Abstract/UserContext/IUserContext.cs @@ -0,0 +1,8 @@ +using AipsCore.Domain.Models.User.ValueObjects; + +namespace AipsCore.Application.Abstract.UserContext; + +public interface IUserContext +{ + UserId GetCurrentUserId(); +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/DI/AipsRegistrationExtensions.cs b/dotnet/AipsCore/Infrastructure/DI/AipsRegistrationExtensions.cs index 7c7e515..a213b5d 100644 --- a/dotnet/AipsCore/Infrastructure/DI/AipsRegistrationExtensions.cs +++ b/dotnet/AipsCore/Infrastructure/DI/AipsRegistrationExtensions.cs @@ -1,8 +1,5 @@ using AipsCore.Application.Abstract; -using AipsCore.Application.Abstract.Command; using AipsCore.Application.Common.Dispatcher; -using AipsCore.Application.Models.User.Command.CreateUser; -using AipsCore.Domain.Models.User.ValueObjects; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -16,6 +13,8 @@ public static class AipsRegistrationExtensions services.AddTransient(); services.AddPersistence(configuration); + + services.AddUserContext(); return services; } diff --git a/dotnet/AipsCore/Infrastructure/DI/UserContextRegistrationExtension.cs b/dotnet/AipsCore/Infrastructure/DI/UserContextRegistrationExtension.cs new file mode 100644 index 0000000..7513690 --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/DI/UserContextRegistrationExtension.cs @@ -0,0 +1,14 @@ +using AipsCore.Application.Abstract.UserContext; +using Microsoft.Extensions.DependencyInjection; + +namespace AipsCore.Infrastructure.DI; + +public static class UserContextRegistrationExtension +{ + public static IServiceCollection AddUserContext(this IServiceCollection services) + { + services.AddTransient(); + + return services; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/UserContext.cs b/dotnet/AipsCore/Infrastructure/UserContext.cs new file mode 100644 index 0000000..8e8ade9 --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/UserContext.cs @@ -0,0 +1,14 @@ +using AipsCore.Application.Abstract.UserContext; +using AipsCore.Domain.Models.User.ValueObjects; + +namespace AipsCore.Infrastructure; + +public class UserContext : IUserContext +{ + public UserId GetCurrentUserId() + { + return new UserId(new Guid("156b58b0-d0f1-4498-b2b6-afa536b68b1a").ToString()); + } +} + +//Ovo je samo trenutno resenje \ No newline at end of file From 40177493c0f41af6286a620ce0e6c1c02de6d3ac Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Wed, 11 Feb 2026 21:59:29 +0100 Subject: [PATCH 4/5] Endpoint for testing functionality --- .../Controllers/WhiteboardController.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dotnet/AipsWebApi/Controllers/WhiteboardController.cs b/dotnet/AipsWebApi/Controllers/WhiteboardController.cs index 0adbbcb..f218187 100644 --- a/dotnet/AipsWebApi/Controllers/WhiteboardController.cs +++ b/dotnet/AipsWebApi/Controllers/WhiteboardController.cs @@ -1,5 +1,8 @@ using AipsCore.Application.Abstract; +using AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard; using AipsCore.Application.Models.Whiteboard.Command.CreateWhiteboard; +using AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard; +using AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard; using Microsoft.AspNetCore.Mvc; namespace AipsWebApi.Controllers; @@ -14,4 +17,25 @@ public class WhiteboardController : ControllerBase var whiteboardId = await dispatcher.Execute(command, cancellationToken); return Ok(whiteboardId.IdValue); } + + [HttpPut("banUser")] + public async Task BanUserFromWhiteboard(BanUserFromWhiteboardCommand command, IDispatcher dispatcher, CancellationToken cancellationToken) + { + await dispatcher.Execute(command, cancellationToken); + return Ok(); + } + + [HttpPut("unbanUser")] + public async Task UnbanUserFromWhiteboard(UnbanUserFromWhiteboardCommand command, IDispatcher dispatcher, CancellationToken cancellationToken) + { + await dispatcher.Execute(command, cancellationToken); + return Ok(); + } + + [HttpPut("kickUser")] + public async Task KickUserFromWhiteboard(KickUserFromWhiteboardCommand command, IDispatcher dispatcher, CancellationToken cancellationToken) + { + await dispatcher.Execute(command, cancellationToken); + return Ok(); + } } \ No newline at end of file From 3f349543d629e49066e76d2750135999867e4c5b Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Wed, 11 Feb 2026 22:53:22 +0100 Subject: [PATCH 5/5] fix --- .../BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs | 2 +- .../BanUserFromWhiteboardCommandErrors.cs | 4 ++-- dotnet/AipsCore/Domain/Common/ValueObjects/DomainId.cs | 5 ----- .../WhiteboardMembership/WhiteboardMembershipRepository.cs | 4 ++-- dotnet/AipsCore/Infrastructure/UserContext.cs | 2 +- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs index e654922..b47059f 100644 --- a/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommand.cs @@ -2,4 +2,4 @@ using AipsCore.Application.Abstract.Command; namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard; -public record BanUserFromWhiteboardCommand(string CallerId, string UserId, string WhiteboardId) : ICommand; \ No newline at end of file +public record BanUserFromWhiteboardCommand(string UserId, string WhiteboardId) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandErrors.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandErrors.cs index b4a2f97..303a529 100644 --- a/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandErrors.cs +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/BanUserFromWhiteboard/BanUserFromWhiteboardCommandErrors.cs @@ -9,10 +9,10 @@ public static class BanUserFromWhiteboardCommandErrors public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId) => new ValidationError( Code: "whiteboard_membership_not_found", - Message: $"User with id '{userId}' is not a member of whiteboard with id '{whiteboardId}'"); + Message: $"User with id '{userId.IdValue}' is not a member of whiteboard with id '{whiteboardId.IdValue}'"); public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId) => new ValidationError( Code: "whiteboard_not_found", - Message: $"Whiteboard with id '{whiteboardId}' not found."); + Message: $"Whiteboard with id '{whiteboardId.IdValue}' not found."); } \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Common/ValueObjects/DomainId.cs b/dotnet/AipsCore/Domain/Common/ValueObjects/DomainId.cs index 9f60074..c638377 100644 --- a/dotnet/AipsCore/Domain/Common/ValueObjects/DomainId.cs +++ b/dotnet/AipsCore/Domain/Common/ValueObjects/DomainId.cs @@ -22,9 +22,4 @@ public record DomainId : AbstractValueObject new MinLengthRule(IdValue, 5) ]; } - - public override string ToString() - { - return IdValue; - } } \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembershipRepository.cs b/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembershipRepository.cs index 58c7952..52bd6ba 100644 --- a/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembershipRepository.cs +++ b/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembershipRepository.cs @@ -57,8 +57,8 @@ public class WhiteboardMembershipRepository { var whiteboardMembership = await Context.WhiteboardMemberships .FirstOrDefaultAsync((entity) => - entity.WhiteboardId.ToString() == whiteboardId.ToString() && - entity.UserId.ToString() == userId.ToString(), + entity.WhiteboardId.ToString() == whiteboardId.IdValue && + entity.UserId.ToString() == userId.IdValue, cancellationToken); if (whiteboardMembership is null) return null; diff --git a/dotnet/AipsCore/Infrastructure/UserContext.cs b/dotnet/AipsCore/Infrastructure/UserContext.cs index 8e8ade9..41bc36e 100644 --- a/dotnet/AipsCore/Infrastructure/UserContext.cs +++ b/dotnet/AipsCore/Infrastructure/UserContext.cs @@ -7,7 +7,7 @@ public class UserContext : IUserContext { public UserId GetCurrentUserId() { - return new UserId(new Guid("156b58b0-d0f1-4498-b2b6-afa536b68b1a").ToString()); + return new UserId(new Guid("52a1810c-802f-48b0-a74c-7b517807e392").ToString()); } }