From f93144f732f525ed03cbaf4240179f73b70b2c6b Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Mon, 9 Feb 2026 00:13:23 +0100 Subject: [PATCH 1/4] Whiteboard Domain --- .../External/IWhiteboardRepository.cs | 11 +++++++ ...etRule.cs => WhiteboardCodeCharsetRule.cs} | 4 +-- .../Validation/WhiteboardTitleCharsetRule.cs | 24 ++++++++++++++ .../Whiteboard/ValueObjects/WhiteboardCode.cs | 7 ++-- .../Whiteboard/ValueObjects/WhiteboardId.cs | 5 ++- .../ValueObjects/WhiteboardTitle.cs | 29 +++++++++++++++++ .../Domain/Models/Whiteboard/Whiteboard.cs | 32 ++++++++++++++++++- 7 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 dotnet/AipsCore/Domain/Models/Whiteboard/External/IWhiteboardRepository.cs rename dotnet/AipsCore/Domain/Models/Whiteboard/Validation/{WhitebordCodeCharsetRule.cs => WhiteboardCodeCharsetRule.cs} (81%) create mode 100644 dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardTitleCharsetRule.cs create mode 100644 dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardTitle.cs diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/External/IWhiteboardRepository.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/External/IWhiteboardRepository.cs new file mode 100644 index 0000000..9de5e1f --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/External/IWhiteboardRepository.cs @@ -0,0 +1,11 @@ +using AipsCore.Domain.Models.Whiteboard.ValueObjects; + +namespace AipsCore.Domain.Models.Whiteboard.External; + +public interface IWhiteboardRepository +{ + Task Get(WhiteboardId whiteboardId, CancellationToken cancellationToken = default); + Task Save(Whiteboard whiteboard, CancellationToken cancellationToken = default); + Task WhiteboardCodeExists(WhiteboardCode whiteboardCode); + +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhitebordCodeCharsetRule.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardCodeCharsetRule.cs similarity index 81% rename from dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhitebordCodeCharsetRule.cs rename to dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardCodeCharsetRule.cs index 2234acc..0627d49 100644 --- a/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhitebordCodeCharsetRule.cs +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardCodeCharsetRule.cs @@ -3,9 +3,9 @@ using AipsCore.Utility.Text; namespace AipsCore.Domain.Models.Whiteboard.Validation; -public class WhitebordCodeCharsetRule : CharsetRule +public class WhiteboardCodeCharsetRule : CharsetRule { - public WhitebordCodeCharsetRule(string stringValue) + public WhiteboardCodeCharsetRule(string stringValue) : base(stringValue, GetWhiteboardCodeCharset()) { } diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardTitleCharsetRule.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardTitleCharsetRule.cs new file mode 100644 index 0000000..1dca215 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardTitleCharsetRule.cs @@ -0,0 +1,24 @@ +using AipsCore.Domain.Common.Validation.Rules; +using AipsCore.Utility.Text; + +namespace AipsCore.Domain.Models.Whiteboard.Validation; + +public class WhiteboardTitleCharsetRule : CharsetRule +{ + public WhiteboardTitleCharsetRule(string stringValue) + : base(stringValue, GetWhiteboardTitleCharset()) + { + + } + + private static char[] GetWhiteboardTitleCharset() + { + var alphanumericCharset = Charset.GetAlphanumericCharset(); + return [..alphanumericCharset, '_', ' ', '/']; + } + + protected override string ErrorCode => "whiteboard_title_charset"; + + protected override string ErrorMessage => + "Whiteboard title contains invalid characters, only alphanumeric characters, '_', ' ', '/' are allowed"; +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardCode.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardCode.cs index 3e7b30a..530f5f1 100644 --- a/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardCode.cs +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardCode.cs @@ -1,9 +1,6 @@ -using AipsCore.Domain.Abstract; -using AipsCore.Domain.Abstract.Rule; +using AipsCore.Domain.Abstract.Rule; using AipsCore.Domain.Abstract.ValueObject; -using AipsCore.Domain.Common.Validation; using AipsCore.Domain.Common.Validation.Rules; -using AipsCore.Domain.Common.ValueObjects; using AipsCore.Domain.Models.Whiteboard.Validation; namespace AipsCore.Domain.Models.Whiteboard.ValueObjects; @@ -25,7 +22,7 @@ public record WhiteboardCode : AbstractValueObject return [ new ExactLength(CodeValue, CodeLength), - new WhitebordCodeCharsetRule(CodeValue) + new WhiteboardCodeCharsetRule(CodeValue) ]; } } \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardId.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardId.cs index a9a46aa..efa8763 100644 --- a/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardId.cs +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardId.cs @@ -2,4 +2,7 @@ namespace AipsCore.Domain.Models.Whiteboard.ValueObjects; -public record WhiteboardId(string IdValue) : DomainId(IdValue); \ No newline at end of file +public record WhiteboardId(string IdValue) : DomainId(IdValue) +{ + public static WhiteboardId Any() => new(Guid.NewGuid().ToString()); +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardTitle.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardTitle.cs new file mode 100644 index 0000000..d637e4e --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/ValueObjects/WhiteboardTitle.cs @@ -0,0 +1,29 @@ +using AipsCore.Domain.Abstract.Rule; +using AipsCore.Domain.Abstract.ValueObject; +using AipsCore.Domain.Common.Validation.Rules; +using AipsCore.Domain.Models.Whiteboard.Validation; + +namespace AipsCore.Domain.Models.Whiteboard.ValueObjects; + +public record WhiteboardTitle : AbstractValueObject +{ + private const int MaxWhiteboardTitleLength = 32; + private const int MinWhiteboardTitleLength = 3; + + public string TitleValue { get; init; } + + public WhiteboardTitle(string TitleValue) + { + this.TitleValue = TitleValue; + Validate(); + } + + protected override ICollection GetValidationRules() + { + return [ + new MaxLengthRule(TitleValue, MaxWhiteboardTitleLength), + new MinLengthRule(TitleValue, MinWhiteboardTitleLength), + new WhiteboardTitleCharsetRule(TitleValue) + ]; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.cs index 1fbb366..3c30185 100644 --- a/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.cs +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.cs @@ -8,11 +8,41 @@ public class Whiteboard public WhiteboardId Id { get; private set; } public UserId WhiteboardOwnerId { get; private set; } public WhiteboardCode Code { get; private set; } + public WhiteboardTitle Title { get; private set; } - public Whiteboard(WhiteboardId id, User.User whiteboardOwner, WhiteboardCode code) + public Whiteboard(WhiteboardId id, User.User whiteboardOwner, WhiteboardCode code, WhiteboardTitle title) { Id = id; WhiteboardOwnerId = whiteboardOwner.Id; Code = code; + Title = title; + } + + public Whiteboard(WhiteboardId id, UserId whiteboardOwnerId, WhiteboardCode code, WhiteboardTitle title) + { + Id = id; + WhiteboardOwnerId = whiteboardOwnerId; + Code = code; + Title = title; + } + + public static Whiteboard Create(string id, string ownerId, string code, string title) + { + var whiteboardId = new WhiteboardId(id); + var whiteboardOwnerId = new UserId(ownerId); + var whiteboardCode = new WhiteboardCode(code); + var whiteboardTitle = new WhiteboardTitle(title); + + return new Whiteboard(whiteboardId, whiteboardOwnerId, whiteboardCode, whiteboardTitle); + } + + public static Whiteboard Create(string ownerId, string code, string title) + { + var whiteboardId = WhiteboardId.Any(); + var whiteboardOwnerId = new UserId(ownerId); + var whiteboardCode = new WhiteboardCode(code); + var whiteboardTitle = new WhiteboardTitle(title); + + return new Whiteboard(whiteboardId, whiteboardOwnerId, whiteboardCode, whiteboardTitle); } } \ No newline at end of file From e9ea07b58a514c5f40ddbb3e69bbac3b0ba1cc7d Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Mon, 9 Feb 2026 00:15:47 +0100 Subject: [PATCH 2/4] Basic create command and handler for whiteboard --- .../CreateWhiteboardCommand.cs | 7 +++ .../CreateWhiteboardCommandHandler.cs | 52 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/CreateWhiteboard/CreateWhiteboardCommand.cs create mode 100644 dotnet/AipsCore/Application/Models/Whiteboard/Command/CreateWhiteboard/CreateWhiteboardCommandHandler.cs diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/CreateWhiteboard/CreateWhiteboardCommand.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/CreateWhiteboard/CreateWhiteboardCommand.cs new file mode 100644 index 0000000..616d7c2 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/CreateWhiteboard/CreateWhiteboardCommand.cs @@ -0,0 +1,7 @@ +using System.Windows.Input; +using AipsCore.Application.Abstract.Command; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; + +namespace AipsCore.Application.Models.Whiteboard.Command.CreateWhiteboard; + +public record CreateWhiteboardCommand(string OwnerId, string Title) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/CreateWhiteboard/CreateWhiteboardCommandHandler.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/CreateWhiteboard/CreateWhiteboardCommandHandler.cs new file mode 100644 index 0000000..f5baf88 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/CreateWhiteboard/CreateWhiteboardCommandHandler.cs @@ -0,0 +1,52 @@ +using AipsCore.Application.Abstract.Command; +using AipsCore.Domain.Abstract; +using AipsCore.Domain.Models.Whiteboard.External; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; + +namespace AipsCore.Application.Models.Whiteboard.Command.CreateWhiteboard; + +public class CreateWhiteboardCommandHandler : ICommandHandler +{ + private readonly IWhiteboardRepository _whiteboardRepository; + private readonly IUnitOfWork _unitOfWork; + + public CreateWhiteboardCommandHandler(IWhiteboardRepository whiteboardRepository, IUnitOfWork unitOfWork) + { + _whiteboardRepository = whiteboardRepository; + _unitOfWork = unitOfWork; + } + + public async Task Handle(CreateWhiteboardCommand command, CancellationToken cancellationToken = default) + { + WhiteboardCode whiteboardCode; + bool codeExists; + + do + { + whiteboardCode = GenerateUniqueWhiteboardCode(); + + codeExists = await _whiteboardRepository.WhiteboardCodeExists(whiteboardCode); + } while (codeExists); + + var whiteboard = Domain.Models.Whiteboard.Whiteboard.Create(command.OwnerId, whiteboardCode.CodeValue, command.Title); + + await _whiteboardRepository.Save(whiteboard, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + + return whiteboard.Id; + } + + // TRENUTNO SAMO, CE SE NAPRAVI KO SERVIS IL KAKO VEC KASNIJE + private static WhiteboardCode GenerateUniqueWhiteboardCode() + { + var rng = new Random(); + char[] result = new char[8]; + + for (int i = 0; i < result.Length; i++) + { + result[i] = (char)('0' + rng.Next(0, 10)); + } + + return new WhiteboardCode(new string(result)); + } +} \ No newline at end of file From e526d25116a078704ed8626efffa7dae0af01a72 Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Mon, 9 Feb 2026 00:18:02 +0100 Subject: [PATCH 3/4] Added EF entity, Migrations, Whiteboard repository service --- .../DI/PersistenceRegistrationExtensions.cs | 3 + .../Persistence/Db/AipsDbContext.cs | 3 +- ...208220239_AddedBasicWhiteboard.Designer.cs | 75 +++++++++++++++++++ .../20260208220239_AddedBasicWhiteboard.cs | 36 +++++++++ .../Migrations/AipsDbContextModelSnapshot.cs | 24 ++++++ .../Persistence/Whiteboard/Whiteboard.cs | 22 ++++++ .../Whiteboard/WhiteboardRepository.cs | 61 +++++++++++++++ 7 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260208220239_AddedBasicWhiteboard.Designer.cs create mode 100644 dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260208220239_AddedBasicWhiteboard.cs create mode 100644 dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/Whiteboard.cs create mode 100644 dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/WhiteboardRepository.cs diff --git a/dotnet/AipsCore/Infrastructure/DI/PersistenceRegistrationExtensions.cs b/dotnet/AipsCore/Infrastructure/DI/PersistenceRegistrationExtensions.cs index 908959f..80c2909 100644 --- a/dotnet/AipsCore/Infrastructure/DI/PersistenceRegistrationExtensions.cs +++ b/dotnet/AipsCore/Infrastructure/DI/PersistenceRegistrationExtensions.cs @@ -1,8 +1,10 @@ using AipsCore.Domain.Abstract; using AipsCore.Domain.Models.User.External; +using AipsCore.Domain.Models.Whiteboard.External; using AipsCore.Infrastructure.DI.Configuration; using AipsCore.Infrastructure.Persistence.Db; using AipsCore.Infrastructure.Persistence.User; +using AipsCore.Infrastructure.Persistence.Whiteboard; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -23,6 +25,7 @@ public static class PersistenceRegistrationExtensions 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 0e95553..cf01403 100644 --- a/dotnet/AipsCore/Infrastructure/Persistence/Db/AipsDbContext.cs +++ b/dotnet/AipsCore/Infrastructure/Persistence/Db/AipsDbContext.cs @@ -5,8 +5,9 @@ namespace AipsCore.Infrastructure.Persistence.Db; public class AipsDbContext : DbContext { public DbSet Users { get; set; } + public DbSet Whiteboards { get; set; } - public AipsDbContext(DbContextOptions options) + public AipsDbContext(DbContextOptions options) : base(options) { diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260208220239_AddedBasicWhiteboard.Designer.cs b/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260208220239_AddedBasicWhiteboard.Designer.cs new file mode 100644 index 0000000..38e15ab --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260208220239_AddedBasicWhiteboard.Designer.cs @@ -0,0 +1,75 @@ +// +using System; +using AipsCore.Infrastructure.Persistence.Db; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AipsCore.Infrastructure.Persistence.Db.Migrations +{ + [DbContext(typeof(AipsDbContext))] + [Migration("20260208220239_AddedBasicWhiteboard")] + partial class AddedBasicWhiteboard + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AipsCore.Infrastructure.Persistence.User.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("WhiteboardOwnerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Whiteboards"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260208220239_AddedBasicWhiteboard.cs b/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260208220239_AddedBasicWhiteboard.cs new file mode 100644 index 0000000..fa4d9db --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260208220239_AddedBasicWhiteboard.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AipsCore.Infrastructure.Persistence.Db.Migrations +{ + /// + public partial class AddedBasicWhiteboard : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Whiteboards", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + WhiteboardOwnerId = table.Column(type: "uuid", nullable: false), + Code = table.Column(type: "character varying(8)", maxLength: 8, nullable: false), + Title = table.Column(type: "character varying(32)", maxLength: 32, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Whiteboards", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Whiteboards"); + } + } +} diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/AipsDbContextModelSnapshot.cs b/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/AipsDbContextModelSnapshot.cs index fa2c82a..f8c68d1 100644 --- a/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/AipsDbContextModelSnapshot.cs +++ b/dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/AipsDbContextModelSnapshot.cs @@ -42,6 +42,30 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations b.ToTable("Users"); }); + + modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("WhiteboardOwnerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Whiteboards"); + }); #pragma warning restore 612, 618 } } diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/Whiteboard.cs b/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/Whiteboard.cs new file mode 100644 index 0000000..df8a199 --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/Whiteboard.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace AipsCore.Infrastructure.Persistence.Whiteboard; + +public class Whiteboard +{ + [Key] + public Guid Id { get; set; } + + [Required] + public Guid WhiteboardOwnerId { get; set; } + + [Required] + [MaxLength(8)] + [MinLength(8)] + public string Code { get; set; } = null!; + + [Required] + [MaxLength(32)] + [MinLength(3)] + public string Title { get; set; } = null!; +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/WhiteboardRepository.cs b/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/WhiteboardRepository.cs new file mode 100644 index 0000000..0620cd7 --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/WhiteboardRepository.cs @@ -0,0 +1,61 @@ +using AipsCore.Domain.Models.Whiteboard.External; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; +using AipsCore.Infrastructure.Persistence.Db; +using Microsoft.EntityFrameworkCore; + +namespace AipsCore.Infrastructure.Persistence.Whiteboard; + +public class WhiteboardRepository : IWhiteboardRepository +{ + private readonly AipsDbContext _context; + + public WhiteboardRepository(AipsDbContext context) + { + _context = context; + } + + public async Task Get(WhiteboardId whiteboardId, CancellationToken cancellationToken = default) + { + var whiteboardEntity = await _context.Whiteboards.FindAsync([new Guid(whiteboardId.IdValue), cancellationToken], cancellationToken: cancellationToken); + + if (whiteboardEntity is null) return null; + + return Domain.Models.Whiteboard.Whiteboard.Create( + whiteboardEntity.Id.ToString(), + whiteboardEntity.WhiteboardOwnerId.ToString(), + whiteboardEntity.Code, + whiteboardEntity.Title); + } + + public async Task Save(Domain.Models.Whiteboard.Whiteboard whiteboard, CancellationToken cancellationToken = default) + { + var whiteboardEntity = await _context.Whiteboards.FindAsync(new Guid(whiteboard.Id.IdValue)); + + if (whiteboardEntity is not null) + { + whiteboardEntity.WhiteboardOwnerId = new Guid(whiteboard.WhiteboardOwnerId.IdValue); + whiteboardEntity.Code = whiteboard.Code.CodeValue; + whiteboardEntity.Title = whiteboard.Title.TitleValue; + + _context.Whiteboards.Update(whiteboardEntity); + } + else + { + whiteboardEntity = new Whiteboard() + { + Id = new Guid(whiteboard.Id.IdValue), + WhiteboardOwnerId = new Guid(whiteboard.WhiteboardOwnerId.IdValue), + Code = whiteboard.Code.CodeValue, + Title = whiteboard.Title.TitleValue, + }; + + _context.Whiteboards.Add(whiteboardEntity); + } + } + + public async Task WhiteboardCodeExists(WhiteboardCode whiteboardCode) + { + var codeExists = await _context.Whiteboards.AnyAsync(w => w.Code == whiteboardCode.CodeValue); + return codeExists; + } +} \ No newline at end of file From 17ab9eb305f45bb36faaa9535b1b75dc55e91391 Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Mon, 9 Feb 2026 00:18:55 +0100 Subject: [PATCH 4/4] Bare bones controller for testing domain entity persistance --- .../Controllers/WhiteboardController.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 dotnet/AipsWebApi/Controllers/WhiteboardController.cs diff --git a/dotnet/AipsWebApi/Controllers/WhiteboardController.cs b/dotnet/AipsWebApi/Controllers/WhiteboardController.cs new file mode 100644 index 0000000..0adbbcb --- /dev/null +++ b/dotnet/AipsWebApi/Controllers/WhiteboardController.cs @@ -0,0 +1,17 @@ +using AipsCore.Application.Abstract; +using AipsCore.Application.Models.Whiteboard.Command.CreateWhiteboard; +using Microsoft.AspNetCore.Mvc; + +namespace AipsWebApi.Controllers; + +[ApiController] +[Route("[controller]")] +public class WhiteboardController : ControllerBase +{ + [HttpPost] + public async Task> CreateWhiteboard(CreateWhiteboardCommand command, IDispatcher dispatcher, CancellationToken cancellationToken) + { + var whiteboardId = await dispatcher.Execute(command, cancellationToken); + return Ok(whiteboardId.IdValue); + } +} \ No newline at end of file