diff --git a/dotnet/AipsCore/AipsCore.csproj b/dotnet/AipsCore/AipsCore.csproj index 0124700..d062ba1 100644 --- a/dotnet/AipsCore/AipsCore.csproj +++ b/dotnet/AipsCore/AipsCore.csproj @@ -17,6 +17,7 @@ + diff --git a/dotnet/AipsCore/Application/Models/WhiteboardMembership/Command/CreateWhiteboardMembership/CreateWhiteboardMembershipCommand.cs b/dotnet/AipsCore/Application/Models/WhiteboardMembership/Command/CreateWhiteboardMembership/CreateWhiteboardMembershipCommand.cs new file mode 100644 index 0000000..00b0665 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/WhiteboardMembership/Command/CreateWhiteboardMembership/CreateWhiteboardMembershipCommand.cs @@ -0,0 +1,13 @@ +using AipsCore.Application.Abstract.Command; +using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +namespace AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership; + +public record CreateWhiteboardMembershipCommand( + string WhiteboardId, + string UserId, + bool IsBanned, + bool EditingEnabled, + bool CanJoin, + DateTime LastInteractedAt) + : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/WhiteboardMembership/Command/CreateWhiteboardMembership/CreateWhiteboardMembershipCommandHandler.cs b/dotnet/AipsCore/Application/Models/WhiteboardMembership/Command/CreateWhiteboardMembership/CreateWhiteboardMembershipCommandHandler.cs new file mode 100644 index 0000000..127419f --- /dev/null +++ b/dotnet/AipsCore/Application/Models/WhiteboardMembership/Command/CreateWhiteboardMembership/CreateWhiteboardMembershipCommandHandler.cs @@ -0,0 +1,34 @@ +using AipsCore.Application.Abstract.Command; +using AipsCore.Domain.Abstract; +using AipsCore.Domain.Models.WhiteboardMembership.External; +using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +namespace AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership; + +public class CreateWhiteboardMembershipCommandHandler : ICommandHandler +{ + private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository; + private readonly IUnitOfWork _unitOfWork; + + public CreateWhiteboardMembershipCommandHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository, IUnitOfWork unitOfWork) + { + _whiteboardMembershipRepository = whiteboardMembershipRepository; + _unitOfWork = unitOfWork; + } + + public async Task Handle(CreateWhiteboardMembershipCommand command, CancellationToken cancellationToken = default) + { + var whiteboardMembership = Domain.Models.WhiteboardMembership.WhiteboardMembership.Create( + command.WhiteboardId, + command.UserId, + command.IsBanned, + command.EditingEnabled, + command.CanJoin, + command.LastInteractedAt); + + await _whiteboardMembershipRepository.Save(whiteboardMembership, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + + return whiteboardMembership.Id; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Common/Validation/Rules/DateInFutureRule.cs b/dotnet/AipsCore/Domain/Common/Validation/Rules/DateInFutureRule.cs new file mode 100644 index 0000000..cf48254 --- /dev/null +++ b/dotnet/AipsCore/Domain/Common/Validation/Rules/DateInFutureRule.cs @@ -0,0 +1,22 @@ +using AipsCore.Domain.Abstract.Rule; + +namespace AipsCore.Domain.Common.Validation.Rules; + +public class DateInFutureRule : AbstractRule +{ + private readonly DateTime _date; + private readonly DateTime _now; + + public DateInFutureRule(DateTime date) + { + _date = date; + _now = DateTime.Now; + } + + protected override string ErrorCode => "date_in_future"; + protected override string ErrorMessage => "Date must be in the future"; + public override bool Validate() + { + return _date > _now; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Common/Validation/Rules/DateInPastRule.cs b/dotnet/AipsCore/Domain/Common/Validation/Rules/DateInPastRule.cs new file mode 100644 index 0000000..2120aa6 --- /dev/null +++ b/dotnet/AipsCore/Domain/Common/Validation/Rules/DateInPastRule.cs @@ -0,0 +1,22 @@ +using AipsCore.Domain.Abstract.Rule; + +namespace AipsCore.Domain.Common.Validation.Rules; + +public class DateInPastRule : AbstractRule +{ + private readonly DateTime _date; + private readonly DateTime _now; + + public DateInPastRule(DateTime date) + { + _date = date; + _now = DateTime.Now; + } + + protected override string ErrorCode => "date_in_past"; + protected override string ErrorMessage => "Date must be in the past"; + public override bool Validate() + { + return _date < _now; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/External/IWhiteboardMembershipRepository.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/External/IWhiteboardMembershipRepository.cs new file mode 100644 index 0000000..2d70762 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/External/IWhiteboardMembershipRepository.cs @@ -0,0 +1,9 @@ +using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +namespace AipsCore.Domain.Models.WhiteboardMembership.External; + +public interface IWhiteboardMembershipRepository +{ + Task Get(WhiteboardMembershipId whiteboardMembershipId, CancellationToken cancellationToken = default); + Task Save(WhiteboardMembership whiteboardMembership, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipCanJoin.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipCanJoin.cs new file mode 100644 index 0000000..48591ad --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipCanJoin.cs @@ -0,0 +1,20 @@ +using AipsCore.Domain.Abstract.Rule; +using AipsCore.Domain.Abstract.ValueObject; + +namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +public record WhiteboardMembershipCanJoin : AbstractValueObject +{ + public bool CanJoinValue { get; init; } + + public WhiteboardMembershipCanJoin(bool CanJoinValue) + { + this.CanJoinValue = CanJoinValue; + Validate(); + } + + protected override ICollection GetValidationRules() + { + return []; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipEditingEnabled.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipEditingEnabled.cs new file mode 100644 index 0000000..da674c1 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipEditingEnabled.cs @@ -0,0 +1,20 @@ +using AipsCore.Domain.Abstract.Rule; +using AipsCore.Domain.Abstract.ValueObject; + +namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +public record WhiteboardMembershipEditingEnabled : AbstractValueObject +{ + public bool EditingEnabledValue { get; init; } + + public WhiteboardMembershipEditingEnabled(bool EditingEnabledValue) + { + this.EditingEnabledValue = EditingEnabledValue; + Validate(); + } + + protected override ICollection GetValidationRules() + { + return []; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipId.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipId.cs new file mode 100644 index 0000000..4f786e0 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipId.cs @@ -0,0 +1,8 @@ +using AipsCore.Domain.Common.ValueObjects; + +namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +public record WhiteboardMembershipId(string IdValue) : DomainId(IdValue) +{ + public static WhiteboardMembershipId Any() => new(Guid.NewGuid().ToString()); +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipIsBanned.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipIsBanned.cs new file mode 100644 index 0000000..363e9ab --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipIsBanned.cs @@ -0,0 +1,20 @@ +using AipsCore.Domain.Abstract.Rule; +using AipsCore.Domain.Abstract.ValueObject; + +namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +public record WhiteboardMembershipIsBanned : AbstractValueObject +{ + public bool IsBannedValue { get; init; } + + public WhiteboardMembershipIsBanned(bool IsBannedValue) + { + this.IsBannedValue = IsBannedValue; + Validate(); + } + + protected override ICollection GetValidationRules() + { + return []; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipLastInteractedAt.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipLastInteractedAt.cs new file mode 100644 index 0000000..b9b8952 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/ValueObjects/WhiteboardMembershipLastInteractedAt.cs @@ -0,0 +1,23 @@ +using AipsCore.Domain.Abstract.Rule; +using AipsCore.Domain.Abstract.ValueObject; +using AipsCore.Domain.Common.Validation.Rules; + +namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +public record WhiteboardMembershipLastInteractedAt : AbstractValueObject +{ + public DateTime LastInteractedAtValue { get; init; } + + public WhiteboardMembershipLastInteractedAt(DateTime LastInteractedAtValue) + { + this.LastInteractedAtValue = LastInteractedAtValue; + Validate(); + } + + protected override ICollection GetValidationRules() + { + return [ + new DateInPastRule(LastInteractedAtValue) + ]; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.cs b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.cs new file mode 100644 index 0000000..6cd006f --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/WhiteboardMembership/WhiteboardMembership.cs @@ -0,0 +1,105 @@ +using AipsCore.Domain.Models.User.ValueObjects; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; +using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; + +namespace AipsCore.Domain.Models.WhiteboardMembership; + +public class WhiteboardMembership +{ + public WhiteboardMembershipId Id { get; private set; } + public WhiteboardId WhiteboardId { get; private set; } + public UserId UserId { get; private set; } + public WhiteboardMembershipIsBanned IsBanned { get; private set; } + public WhiteboardMembershipEditingEnabled EditingEnabled { get; private set; } + public WhiteboardMembershipCanJoin CanJoin { get; private set; } + public WhiteboardMembershipLastInteractedAt LastInteractedAt { get; private set; } + + public WhiteboardMembership( + WhiteboardMembershipId id, + Whiteboard.Whiteboard owner, + User.User user, + WhiteboardMembershipIsBanned isBanned, + WhiteboardMembershipEditingEnabled editingEnabled, + WhiteboardMembershipCanJoin canJoin, + WhiteboardMembershipLastInteractedAt lastInteractedAt) + { + Id = id; + WhiteboardId = owner.Id; + UserId = user.Id; + IsBanned = isBanned; + EditingEnabled = editingEnabled; + CanJoin = canJoin; + LastInteractedAt = lastInteractedAt; + } + + public WhiteboardMembership( + WhiteboardMembershipId id, + WhiteboardId ownerId, + UserId userId, + WhiteboardMembershipIsBanned isBanned, + WhiteboardMembershipEditingEnabled editingEnabled, + WhiteboardMembershipCanJoin canJoin, + WhiteboardMembershipLastInteractedAt lastInteractedAt) + { + Id = id; + WhiteboardId = ownerId; + UserId = userId; + IsBanned = isBanned; + EditingEnabled = editingEnabled; + CanJoin = canJoin; + LastInteractedAt = lastInteractedAt; + } + + public static WhiteboardMembership Create( + string id, + string ownerId, + string userId, + bool isBanned, + bool editingEnabled, + bool canJoin, + DateTime lastInteractedAt) + { + var whiteboardMembershipId = new WhiteboardMembershipId(id); + var whiteboardId = new WhiteboardId(ownerId); + var userIdVo = new UserId(userId); + var isBannedVo = new WhiteboardMembershipIsBanned(isBanned); + var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled); + var canJoinVo = new WhiteboardMembershipCanJoin(canJoin); + var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt); + + return new WhiteboardMembership( + whiteboardMembershipId, + whiteboardId, + userIdVo, + isBannedVo, + editingEnabledVo, + canJoinVo, + lastInteractedAtVo); + } + + public static WhiteboardMembership Create( + string ownerId, + string userId, + bool isBanned, + bool editingEnabled, + bool canJoin, + DateTime lastInteractedAt) + { + var whiteboardMembershipId = WhiteboardMembershipId.Any(); + var whiteboardId = new WhiteboardId(ownerId); + var userIdVo = new UserId(userId); + var isBannedVo = new WhiteboardMembershipIsBanned(isBanned); + var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled); + var canJoinVo = new WhiteboardMembershipCanJoin(canJoin); + var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt); + + return new WhiteboardMembership( + whiteboardMembershipId, + whiteboardId, + userIdVo, + isBannedVo, + editingEnabledVo, + canJoinVo, + lastInteractedAtVo); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/DI/PersistenceRegistrationExtensions.cs b/dotnet/AipsCore/Infrastructure/DI/PersistenceRegistrationExtensions.cs index 80c2909..182f7e7 100644 --- a/dotnet/AipsCore/Infrastructure/DI/PersistenceRegistrationExtensions.cs +++ b/dotnet/AipsCore/Infrastructure/DI/PersistenceRegistrationExtensions.cs @@ -1,6 +1,7 @@ using AipsCore.Domain.Abstract; using AipsCore.Domain.Models.User.External; using AipsCore.Domain.Models.Whiteboard.External; +using AipsCore.Domain.Models.WhiteboardMembership.External; using AipsCore.Infrastructure.DI.Configuration; using AipsCore.Infrastructure.Persistence.Db; using AipsCore.Infrastructure.Persistence.User; @@ -26,6 +27,7 @@ public static class PersistenceRegistrationExtensions services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); return services; } diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Db/AipsDbContext.cs b/dotnet/AipsCore/Infrastructure/Persistence/Db/AipsDbContext.cs index 8623f8e..cbe5e5d 100644 --- a/dotnet/AipsCore/Infrastructure/Persistence/Db/AipsDbContext.cs +++ b/dotnet/AipsCore/Infrastructure/Persistence/Db/AipsDbContext.cs @@ -7,6 +7,7 @@ public class AipsDbContext : DbContext public DbSet Users { get; set; } public DbSet Whiteboards { get; set; } public DbSet Shapes { get; set; } + public DbSet WhiteboardMemberships { get; set; } public AipsDbContext(DbContextOptions options) : base(options) diff --git a/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembership.cs b/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembership.cs new file mode 100644 index 0000000..c2cd8d8 --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembership.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; + +namespace AipsCore.Infrastructure.Persistence.WhiteboardMembership; + +public class WhiteboardMembership +{ + [Key] + public Guid Id { get; set; } + + public Guid WhiteboardId { get; set; } + + public Whiteboard.Whiteboard? Whiteboard { get; set; } = null!; + + public Guid UserId { get; set; } + + public User.User? User { get; set; } = null!; + + public bool IsBanned { get; set; } + + public bool EditingEnabled { get; set; } + + public bool CanJoin { get; set; } + + public DateTime LastInteractedAt { get; set; } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembershipRepository.cs b/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembershipRepository.cs new file mode 100644 index 0000000..1913283 --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/Persistence/WhiteboardMembership/WhiteboardMembershipRepository.cs @@ -0,0 +1,63 @@ +using AipsCore.Domain.Models.WhiteboardMembership.External; +using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects; +using AipsCore.Infrastructure.Persistence.Db; + +namespace AipsCore.Infrastructure.Persistence.WhiteboardMembership; + +public class WhiteboardMembershipRepository : IWhiteboardMembershipRepository +{ + private readonly AipsDbContext _context; + + public WhiteboardMembershipRepository(AipsDbContext context) + { + _context = context; + } + + public async Task Get(WhiteboardMembershipId whiteboardMembershipId, CancellationToken cancellationToken = default) + { + var whiteboardMembershipEntity = await _context.WhiteboardMemberships.FindAsync(new Guid(whiteboardMembershipId.IdValue)); + + if (whiteboardMembershipEntity is null) return null; + + return Domain.Models.WhiteboardMembership.WhiteboardMembership.Create( + whiteboardMembershipEntity.Id.ToString(), + whiteboardMembershipEntity.WhiteboardId.ToString(), + whiteboardMembershipEntity.UserId.ToString(), + whiteboardMembershipEntity.IsBanned, + whiteboardMembershipEntity.EditingEnabled, + whiteboardMembershipEntity.CanJoin, + whiteboardMembershipEntity.LastInteractedAt); + } + + public async Task Save(Domain.Models.WhiteboardMembership.WhiteboardMembership whiteboardMembership, CancellationToken cancellationToken = default) + { + var whiteboardMembershipEntity = await _context.WhiteboardMemberships.FindAsync(new Guid(whiteboardMembership.Id.IdValue)); + + if (whiteboardMembershipEntity is not null) + { + whiteboardMembershipEntity.IsBanned = whiteboardMembership.IsBanned.IsBannedValue; + whiteboardMembershipEntity.EditingEnabled = whiteboardMembership.EditingEnabled.EditingEnabledValue; + whiteboardMembershipEntity.CanJoin = whiteboardMembership.CanJoin.CanJoinValue; + whiteboardMembershipEntity.LastInteractedAt = whiteboardMembership.LastInteractedAt.LastInteractedAtValue; + + _context.Update(whiteboardMembershipEntity); + } + else + { + whiteboardMembershipEntity = new WhiteboardMembership() + { + Id = new Guid(whiteboardMembership.Id.IdValue), + WhiteboardId = new Guid(whiteboardMembership.WhiteboardId.IdValue), + Whiteboard = null, + UserId = new Guid(whiteboardMembership.UserId.IdValue), + User = null, + IsBanned = whiteboardMembership.IsBanned.IsBannedValue, + EditingEnabled = whiteboardMembership.EditingEnabled.EditingEnabledValue, + CanJoin = whiteboardMembership.CanJoin.CanJoinValue, + LastInteractedAt = whiteboardMembership.LastInteractedAt.LastInteractedAtValue + }; + + _context.Add(whiteboardMembershipEntity); + } + } +} \ No newline at end of file