From ef3cc482340419c8d761ac75e08b7512dfe08eea Mon Sep 17 00:00:00 2001 From: DavidLazarescu Date: Mon, 11 Sep 2023 16:42:51 +0200 Subject: [PATCH] Added highlights --- .../Common/DTOs/Books/BookOutDto.cs | 3 + .../Common/DTOs/Highlights/HighlightInDto.cs | 17 + .../Common/DTOs/Highlights/HighlightOutDto.cs | 12 + .../Common/DTOs/Highlights/RectFInDto.cs | 17 + .../Common/DTOs/Highlights/RectFOutDto.cs | 12 + .../Mappings/HighlightAutoMapperProfile.cs | 17 + .../Common/Mappings/RectFAutoMapperProfile.cs | 14 + .../Repositories/IHighlightRepository.cs | 11 + .../Interfaces/Services/IHighlightService.cs | 9 + src/Application/Services/HighlightService.cs | 72 +++ src/Domain/Entities/Book.cs | 2 + src/Domain/Entities/Highlight.cs | 23 + src/Domain/Entities/RectF.cs | 24 + src/Infrastructure/Persistence/DataContext.cs | 1 + ...20230911113759_AddedHighlights.Designer.cs | 533 ++++++++++++++++++ .../20230911113759_AddedHighlights.cs | 74 +++ ...11141116_FixHighlightReference.Designer.cs | 532 +++++++++++++++++ .../20230911141116_FixHighlightReference.cs | 85 +++ .../Migrations/DataContextModelSnapshot.cs | 79 +++ .../Persistence/Repository/BookRepository.cs | 8 +- .../Repository/HighlightRepository.cs | 39 ++ .../Controllers/HighlightController.cs | 57 ++ src/Presentation/DependencyInjection.cs | 2 + 23 files changed, 1642 insertions(+), 1 deletion(-) create mode 100644 src/Application/Common/DTOs/Highlights/HighlightInDto.cs create mode 100644 src/Application/Common/DTOs/Highlights/HighlightOutDto.cs create mode 100644 src/Application/Common/DTOs/Highlights/RectFInDto.cs create mode 100644 src/Application/Common/DTOs/Highlights/RectFOutDto.cs create mode 100644 src/Application/Common/Mappings/HighlightAutoMapperProfile.cs create mode 100644 src/Application/Common/Mappings/RectFAutoMapperProfile.cs create mode 100644 src/Application/Interfaces/Repositories/IHighlightRepository.cs create mode 100644 src/Application/Interfaces/Services/IHighlightService.cs create mode 100644 src/Application/Services/HighlightService.cs create mode 100644 src/Domain/Entities/Highlight.cs create mode 100644 src/Domain/Entities/RectF.cs create mode 100644 src/Infrastructure/Persistence/Migrations/20230911113759_AddedHighlights.Designer.cs create mode 100644 src/Infrastructure/Persistence/Migrations/20230911113759_AddedHighlights.cs create mode 100644 src/Infrastructure/Persistence/Migrations/20230911141116_FixHighlightReference.Designer.cs create mode 100644 src/Infrastructure/Persistence/Migrations/20230911141116_FixHighlightReference.cs create mode 100644 src/Infrastructure/Persistence/Repository/HighlightRepository.cs create mode 100644 src/Presentation/Controllers/HighlightController.cs diff --git a/src/Application/Common/DTOs/Books/BookOutDto.cs b/src/Application/Common/DTOs/Books/BookOutDto.cs index 7aeec21..0a48815 100644 --- a/src/Application/Common/DTOs/Books/BookOutDto.cs +++ b/src/Application/Common/DTOs/Books/BookOutDto.cs @@ -1,3 +1,4 @@ +using Application.Common.DTOs.Highlights; using Application.Common.DTOs.Tags; namespace Application.Common.DTOs.Books; @@ -38,4 +39,6 @@ public class BookOutDto public ICollection Tags { get; set; } = new List(); + + public ICollection Highlights { get; set; } = new List(); } \ No newline at end of file diff --git a/src/Application/Common/DTOs/Highlights/HighlightInDto.cs b/src/Application/Common/DTOs/Highlights/HighlightInDto.cs new file mode 100644 index 0000000..ec924a7 --- /dev/null +++ b/src/Application/Common/DTOs/Highlights/HighlightInDto.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +namespace Application.Common.DTOs.Highlights; + +public class HighlightInDto +{ + [Required] + public Guid Guid { get; set; } + + [Required] + public string Color { get; set; } + + [Required] + public int PageNumber { get; set; } + + [Required] + public ICollection Rects { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Application/Common/DTOs/Highlights/HighlightOutDto.cs b/src/Application/Common/DTOs/Highlights/HighlightOutDto.cs new file mode 100644 index 0000000..9ab7535 --- /dev/null +++ b/src/Application/Common/DTOs/Highlights/HighlightOutDto.cs @@ -0,0 +1,12 @@ +namespace Application.Common.DTOs.Highlights; + +public class HighlightOutDto +{ + public string Guid { get; set; } + + public string Color { get; set; } + + public int PageNumber { get; set; } + + public ICollection Rects { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Application/Common/DTOs/Highlights/RectFInDto.cs b/src/Application/Common/DTOs/Highlights/RectFInDto.cs new file mode 100644 index 0000000..9187ab1 --- /dev/null +++ b/src/Application/Common/DTOs/Highlights/RectFInDto.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +namespace Application.Common.DTOs.Highlights; + +public class RectFInDto +{ + [Required] + public float X { get; set; } + + [Required] + public float Y { get; set; } + + [Required] + public float Width { get; set; } + + [Required] + public float Height { get; set; } +} \ No newline at end of file diff --git a/src/Application/Common/DTOs/Highlights/RectFOutDto.cs b/src/Application/Common/DTOs/Highlights/RectFOutDto.cs new file mode 100644 index 0000000..1152725 --- /dev/null +++ b/src/Application/Common/DTOs/Highlights/RectFOutDto.cs @@ -0,0 +1,12 @@ +namespace Application.Common.DTOs.Highlights; + +public class RectFOutDto +{ + public float X { get; set; } + + public float Y { get; set; } + + public float Width { get; set; } + + public float Height { get; set; } +} \ No newline at end of file diff --git a/src/Application/Common/Mappings/HighlightAutoMapperProfile.cs b/src/Application/Common/Mappings/HighlightAutoMapperProfile.cs new file mode 100644 index 0000000..8a388f4 --- /dev/null +++ b/src/Application/Common/Mappings/HighlightAutoMapperProfile.cs @@ -0,0 +1,17 @@ +using Application.Common.DTOs.Highlights; +using Application.Common.DTOs.Tags; +using AutoMapper; +using Domain.Entities; + +namespace Application.Common.Mappings; + +public class HighlightAutoMapperProfile : Profile +{ + public HighlightAutoMapperProfile() + { + CreateMap() + .ForMember(dest => dest.HighlightId, temp => temp.MapFrom(src => src.Guid)); + CreateMap() + .ForMember(dest => dest.Guid, temp => temp.MapFrom(src => src.HighlightId.ToString())); + } +} \ No newline at end of file diff --git a/src/Application/Common/Mappings/RectFAutoMapperProfile.cs b/src/Application/Common/Mappings/RectFAutoMapperProfile.cs new file mode 100644 index 0000000..3ad93df --- /dev/null +++ b/src/Application/Common/Mappings/RectFAutoMapperProfile.cs @@ -0,0 +1,14 @@ +using Application.Common.DTOs.Highlights; +using AutoMapper; +using Domain.Entities; + +namespace Application.Common.Mappings; + +public class RectFAutoMapperProfile : Profile +{ + public RectFAutoMapperProfile() + { + CreateMap(); + CreateMap(); + } +} \ No newline at end of file diff --git a/src/Application/Interfaces/Repositories/IHighlightRepository.cs b/src/Application/Interfaces/Repositories/IHighlightRepository.cs new file mode 100644 index 0000000..0bf1755 --- /dev/null +++ b/src/Application/Interfaces/Repositories/IHighlightRepository.cs @@ -0,0 +1,11 @@ +using Domain.Entities; + +namespace Application.Interfaces.Repositories; + +public interface IHighlightRepository +{ + public Task SaveChangesAsync(); + void Add(Highlight highlight); + void Delete(Highlight highlight); + Task GetAsync(Guid bookId, Guid highlightGuid); +} \ No newline at end of file diff --git a/src/Application/Interfaces/Services/IHighlightService.cs b/src/Application/Interfaces/Services/IHighlightService.cs new file mode 100644 index 0000000..82fa716 --- /dev/null +++ b/src/Application/Interfaces/Services/IHighlightService.cs @@ -0,0 +1,9 @@ +using Application.Common.DTOs.Highlights; + +namespace Application.Interfaces.Services; + +public interface IHighlightService +{ + Task CreateHighlightAsync(string email, Guid bookGuid, HighlightInDto highlightIn); + Task DeleteHighlightAsync(string email, Guid bookGuid, Guid highlightGuid); +} \ No newline at end of file diff --git a/src/Application/Services/HighlightService.cs b/src/Application/Services/HighlightService.cs new file mode 100644 index 0000000..9c74b99 --- /dev/null +++ b/src/Application/Services/HighlightService.cs @@ -0,0 +1,72 @@ +using Application.Common.DTOs.Highlights; +using Application.Common.Exceptions; +using Application.Interfaces.Repositories; +using Application.Interfaces.Services; +using AutoMapper; +using Domain.Entities; + +namespace Application.Services; + +public class HighlightService : IHighlightService +{ + private readonly IUserRepository _userRepository; + private readonly IMapper _mapper; + private readonly IHighlightRepository _highlightRepository; + private readonly IBookRepository _bookRepository; + + + public HighlightService(IMapper mapper, + IHighlightRepository highlightRepository, + IBookRepository bookRepository, + IUserRepository userRepository) + { + _mapper = mapper; + _highlightRepository = highlightRepository; + _bookRepository = bookRepository; + _userRepository = userRepository; + } + + + public async Task CreateHighlightAsync(string email, Guid bookGuid, HighlightInDto highlightIn) + { + var user = await _userRepository.GetAsync(email, trackChanges: true); + var book = user.Books.SingleOrDefault(book => book.BookId == bookGuid); + if (book == default) + { + const string message = "No book with this id exists"; + throw new CommonErrorException(404, message, 4); + } + + var highlightExists = book.Highlights.Any(h => h.HighlightId == highlightIn.Guid); + if(highlightExists) + { + const string message = "A highlight with this id already exists"; + throw new CommonErrorException(409, message, 0); + } + + var newHighlight = _mapper.Map(highlightIn); + newHighlight.Book = book; + book.Highlights.Add(newHighlight); + + await _highlightRepository.SaveChangesAsync(); + } + + public async Task DeleteHighlightAsync(string email, Guid bookGuid, Guid highlightGuid) + { + var user = await _userRepository.GetAsync(email, trackChanges: true); + var bookExists = await _bookRepository.ExistsAsync(user.Id, bookGuid); + if (!bookExists) + return; + + var highlight = await _highlightRepository.GetAsync(bookGuid, + highlightGuid); + if (highlight == default) + { + const string message = "No highlight with this id exists"; + throw new CommonErrorException(404, message, 0); + } + + _highlightRepository.Delete(highlight); + await _highlightRepository.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/src/Domain/Entities/Book.cs b/src/Domain/Entities/Book.cs index cdc6149..1ed2d46 100644 --- a/src/Domain/Entities/Book.cs +++ b/src/Domain/Entities/Book.cs @@ -69,6 +69,8 @@ public class Book public ICollection Tags { get; set; } = new List(); + public ICollection Highlights { get; set; } = new List(); + public string UserId { get; set; } public User User { get; set; } } diff --git a/src/Domain/Entities/Highlight.cs b/src/Domain/Entities/Highlight.cs new file mode 100644 index 0000000..b54bfa8 --- /dev/null +++ b/src/Domain/Entities/Highlight.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Domain.Entities; + +public class Highlight +{ + [DatabaseGenerated(DatabaseGeneratedOption.None)] + [Key] + public Guid HighlightId { get; set; } + + [Required] + public string Color { get; set; } + + [Required] + public int PageNumber { get; set; } + + [Required] + public ICollection Rects { get; set; } = new List(); + + public Guid BookId { get; set; } + public Book Book { get; set; } +} \ No newline at end of file diff --git a/src/Domain/Entities/RectF.cs b/src/Domain/Entities/RectF.cs new file mode 100644 index 0000000..670fc2b --- /dev/null +++ b/src/Domain/Entities/RectF.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; + +namespace Domain.Entities; + +public class RectF +{ + [Key] + public Guid RectFId { get; set; } + + [Required] + public float X { get; set; } + + [Required] + public float Y { get; set; } + + [Required] + public float Width { get; set; } + + [Required] + public float Height { get; set; } + + public Guid HighlightId { get; set; } + public Highlight Highlight { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure/Persistence/DataContext.cs b/src/Infrastructure/Persistence/DataContext.cs index e876a99..a989f01 100644 --- a/src/Infrastructure/Persistence/DataContext.cs +++ b/src/Infrastructure/Persistence/DataContext.cs @@ -8,6 +8,7 @@ namespace Infrastructure.Persistence; public class DataContext : IdentityDbContext { public DbSet Books { get; set; } + public DbSet Highlights { get; set; } public DbSet Tags { get; set; } diff --git a/src/Infrastructure/Persistence/Migrations/20230911113759_AddedHighlights.Designer.cs b/src/Infrastructure/Persistence/Migrations/20230911113759_AddedHighlights.Designer.cs new file mode 100644 index 0000000..e160db8 --- /dev/null +++ b/src/Infrastructure/Persistence/Migrations/20230911113759_AddedHighlights.Designer.cs @@ -0,0 +1,533 @@ +// +using System; +using Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230911113759_AddedHighlights")] + partial class AddedHighlights + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Domain.Entities.Book", b => + { + b.Property("BookId") + .HasColumnType("uniqueidentifier"); + + b.Property("AddedToLibrary") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Authors") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("CoverLastModified") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CoverSize") + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasMaxLength(140) + .HasColumnType("nvarchar(140)"); + + b.Property("Creator") + .HasMaxLength(140) + .HasColumnType("nvarchar(140)"); + + b.Property("CurrentPage") + .HasColumnType("int"); + + b.Property("DocumentSize") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Format") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("HasCover") + .HasColumnType("bit"); + + b.Property("Language") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastModified") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastOpened") + .HasColumnType("nvarchar(max)"); + + b.Property("PageCount") + .HasColumnType("int"); + + b.Property("PagesSize") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("BookId"); + + b.HasIndex("UserId"); + + b.ToTable("Books"); + }); + + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.Property("HighlightId") + .HasColumnType("uniqueidentifier"); + + b.Property("BookId") + .HasColumnType("nvarchar(max)"); + + b.Property("BookId1") + .HasColumnType("uniqueidentifier"); + + b.Property("Color") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PageNumber") + .HasColumnType("int"); + + b.HasKey("HighlightId"); + + b.HasIndex("BookId1"); + + b.ToTable("Highlights"); + }); + + modelBuilder.Entity("Domain.Entities.RectF", b => + { + b.Property("RectFId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Height") + .HasColumnType("real"); + + b.Property("HighlightId") + .HasColumnType("uniqueidentifier"); + + b.Property("Width") + .HasColumnType("real"); + + b.Property("X") + .HasColumnType("real"); + + b.Property("Y") + .HasColumnType("real"); + + b.HasKey("RectFId"); + + b.HasIndex("HighlightId"); + + b.ToTable("RectF"); + }); + + modelBuilder.Entity("Domain.Entities.Tag", b => + { + b.Property("TagId") + .HasColumnType("uniqueidentifier"); + + b.Property("BookId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("TagId"); + + b.HasIndex("BookId"); + + b.HasIndex("UserId"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("AccountCreation") + .HasColumnType("datetime2"); + + b.Property("BookStorageLimit") + .HasColumnType("bigint"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("HasProfilePicture") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("ProfilePictureLastUpdated") + .HasColumnType("datetime2"); + + b.Property("Role") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.Book", b => + { + b.HasOne("Domain.Entities.User", "User") + .WithMany("Books") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.HasOne("Domain.Entities.Book", "Book") + .WithMany("Highlights") + .HasForeignKey("BookId1"); + + b.Navigation("Book"); + }); + + modelBuilder.Entity("Domain.Entities.RectF", b => + { + b.HasOne("Domain.Entities.Highlight", "Highlight") + .WithMany("Rects") + .HasForeignKey("HighlightId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Highlight"); + }); + + modelBuilder.Entity("Domain.Entities.Tag", b => + { + b.HasOne("Domain.Entities.Book", null) + .WithMany("Tags") + .HasForeignKey("BookId"); + + b.HasOne("Domain.Entities.User", "User") + .WithMany("Tags") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Entities.Book", b => + { + b.Navigation("Highlights"); + + b.Navigation("Tags"); + }); + + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.Navigation("Rects"); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Navigation("Books"); + + b.Navigation("Tags"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Persistence/Migrations/20230911113759_AddedHighlights.cs b/src/Infrastructure/Persistence/Migrations/20230911113759_AddedHighlights.cs new file mode 100644 index 0000000..8dbf656 --- /dev/null +++ b/src/Infrastructure/Persistence/Migrations/20230911113759_AddedHighlights.cs @@ -0,0 +1,74 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Persistence.Migrations +{ + public partial class AddedHighlights : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Highlights", + columns: table => new + { + HighlightId = table.Column(type: "uniqueidentifier", nullable: false), + Color = table.Column(type: "nvarchar(max)", nullable: false), + PageNumber = table.Column(type: "int", nullable: false), + BookId = table.Column(type: "nvarchar(max)", nullable: true), + BookId1 = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Highlights", x => x.HighlightId); + table.ForeignKey( + name: "FK_Highlights_Books_BookId1", + column: x => x.BookId1, + principalTable: "Books", + principalColumn: "BookId"); + }); + + migrationBuilder.CreateTable( + name: "RectF", + columns: table => new + { + RectFId = table.Column(type: "uniqueidentifier", nullable: false), + X = table.Column(type: "real", nullable: false), + Y = table.Column(type: "real", nullable: false), + Width = table.Column(type: "real", nullable: false), + Height = table.Column(type: "real", nullable: false), + HighlightId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RectF", x => x.RectFId); + table.ForeignKey( + name: "FK_RectF_Highlights_HighlightId", + column: x => x.HighlightId, + principalTable: "Highlights", + principalColumn: "HighlightId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Highlights_BookId1", + table: "Highlights", + column: "BookId1"); + + migrationBuilder.CreateIndex( + name: "IX_RectF_HighlightId", + table: "RectF", + column: "HighlightId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "RectF"); + + migrationBuilder.DropTable( + name: "Highlights"); + } + } +} diff --git a/src/Infrastructure/Persistence/Migrations/20230911141116_FixHighlightReference.Designer.cs b/src/Infrastructure/Persistence/Migrations/20230911141116_FixHighlightReference.Designer.cs new file mode 100644 index 0000000..72ec545 --- /dev/null +++ b/src/Infrastructure/Persistence/Migrations/20230911141116_FixHighlightReference.Designer.cs @@ -0,0 +1,532 @@ +// +using System; +using Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230911141116_FixHighlightReference")] + partial class FixHighlightReference + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Domain.Entities.Book", b => + { + b.Property("BookId") + .HasColumnType("uniqueidentifier"); + + b.Property("AddedToLibrary") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Authors") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("CoverLastModified") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CoverSize") + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasMaxLength(140) + .HasColumnType("nvarchar(140)"); + + b.Property("Creator") + .HasMaxLength(140) + .HasColumnType("nvarchar(140)"); + + b.Property("CurrentPage") + .HasColumnType("int"); + + b.Property("DocumentSize") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Format") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("HasCover") + .HasColumnType("bit"); + + b.Property("Language") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastModified") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastOpened") + .HasColumnType("nvarchar(max)"); + + b.Property("PageCount") + .HasColumnType("int"); + + b.Property("PagesSize") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("BookId"); + + b.HasIndex("UserId"); + + b.ToTable("Books"); + }); + + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.Property("HighlightId") + .HasColumnType("uniqueidentifier"); + + b.Property("BookId") + .HasColumnType("uniqueidentifier"); + + b.Property("Color") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PageNumber") + .HasColumnType("int"); + + b.HasKey("HighlightId"); + + b.HasIndex("BookId"); + + b.ToTable("Highlights"); + }); + + modelBuilder.Entity("Domain.Entities.RectF", b => + { + b.Property("RectFId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Height") + .HasColumnType("real"); + + b.Property("HighlightId") + .HasColumnType("uniqueidentifier"); + + b.Property("Width") + .HasColumnType("real"); + + b.Property("X") + .HasColumnType("real"); + + b.Property("Y") + .HasColumnType("real"); + + b.HasKey("RectFId"); + + b.HasIndex("HighlightId"); + + b.ToTable("RectF"); + }); + + modelBuilder.Entity("Domain.Entities.Tag", b => + { + b.Property("TagId") + .HasColumnType("uniqueidentifier"); + + b.Property("BookId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("TagId"); + + b.HasIndex("BookId"); + + b.HasIndex("UserId"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("AccountCreation") + .HasColumnType("datetime2"); + + b.Property("BookStorageLimit") + .HasColumnType("bigint"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("HasProfilePicture") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("ProfilePictureLastUpdated") + .HasColumnType("datetime2"); + + b.Property("Role") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.Book", b => + { + b.HasOne("Domain.Entities.User", "User") + .WithMany("Books") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.HasOne("Domain.Entities.Book", "Book") + .WithMany("Highlights") + .HasForeignKey("BookId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Book"); + }); + + modelBuilder.Entity("Domain.Entities.RectF", b => + { + b.HasOne("Domain.Entities.Highlight", "Highlight") + .WithMany("Rects") + .HasForeignKey("HighlightId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Highlight"); + }); + + modelBuilder.Entity("Domain.Entities.Tag", b => + { + b.HasOne("Domain.Entities.Book", null) + .WithMany("Tags") + .HasForeignKey("BookId"); + + b.HasOne("Domain.Entities.User", "User") + .WithMany("Tags") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Entities.Book", b => + { + b.Navigation("Highlights"); + + b.Navigation("Tags"); + }); + + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.Navigation("Rects"); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Navigation("Books"); + + b.Navigation("Tags"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Persistence/Migrations/20230911141116_FixHighlightReference.cs b/src/Infrastructure/Persistence/Migrations/20230911141116_FixHighlightReference.cs new file mode 100644 index 0000000..eb5f95c --- /dev/null +++ b/src/Infrastructure/Persistence/Migrations/20230911141116_FixHighlightReference.cs @@ -0,0 +1,85 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Persistence.Migrations +{ + public partial class FixHighlightReference : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Highlights_Books_BookId1", + table: "Highlights"); + + migrationBuilder.DropIndex( + name: "IX_Highlights_BookId1", + table: "Highlights"); + + migrationBuilder.DropColumn( + name: "BookId1", + table: "Highlights"); + + migrationBuilder.AlterColumn( + name: "BookId", + table: "Highlights", + type: "uniqueidentifier", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Highlights_BookId", + table: "Highlights", + column: "BookId"); + + migrationBuilder.AddForeignKey( + name: "FK_Highlights_Books_BookId", + table: "Highlights", + column: "BookId", + principalTable: "Books", + principalColumn: "BookId", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Highlights_Books_BookId", + table: "Highlights"); + + migrationBuilder.DropIndex( + name: "IX_Highlights_BookId", + table: "Highlights"); + + migrationBuilder.AlterColumn( + name: "BookId", + table: "Highlights", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uniqueidentifier"); + + migrationBuilder.AddColumn( + name: "BookId1", + table: "Highlights", + type: "uniqueidentifier", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Highlights_BookId1", + table: "Highlights", + column: "BookId1"); + + migrationBuilder.AddForeignKey( + name: "FK_Highlights_Books_BookId1", + table: "Highlights", + column: "BookId1", + principalTable: "Books", + principalColumn: "BookId"); + } + } +} diff --git a/src/Infrastructure/Persistence/Migrations/DataContextModelSnapshot.cs b/src/Infrastructure/Persistence/Migrations/DataContextModelSnapshot.cs index cd6c119..f52992a 100644 --- a/src/Infrastructure/Persistence/Migrations/DataContextModelSnapshot.cs +++ b/src/Infrastructure/Persistence/Migrations/DataContextModelSnapshot.cs @@ -101,6 +101,56 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Books"); }); + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.Property("HighlightId") + .HasColumnType("uniqueidentifier"); + + b.Property("BookId") + .HasColumnType("uniqueidentifier"); + + b.Property("Color") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PageNumber") + .HasColumnType("int"); + + b.HasKey("HighlightId"); + + b.HasIndex("BookId"); + + b.ToTable("Highlights"); + }); + + modelBuilder.Entity("Domain.Entities.RectF", b => + { + b.Property("RectFId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Height") + .HasColumnType("real"); + + b.Property("HighlightId") + .HasColumnType("uniqueidentifier"); + + b.Property("Width") + .HasColumnType("real"); + + b.Property("X") + .HasColumnType("real"); + + b.Property("Y") + .HasColumnType("real"); + + b.HasKey("RectFId"); + + b.HasIndex("HighlightId"); + + b.ToTable("RectF"); + }); + modelBuilder.Entity("Domain.Entities.Tag", b => { b.Property("TagId") @@ -368,6 +418,28 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("User"); }); + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.HasOne("Domain.Entities.Book", "Book") + .WithMany("Highlights") + .HasForeignKey("BookId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Book"); + }); + + modelBuilder.Entity("Domain.Entities.RectF", b => + { + b.HasOne("Domain.Entities.Highlight", "Highlight") + .WithMany("Rects") + .HasForeignKey("HighlightId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Highlight"); + }); + modelBuilder.Entity("Domain.Entities.Tag", b => { b.HasOne("Domain.Entities.Book", null) @@ -436,9 +508,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Domain.Entities.Book", b => { + b.Navigation("Highlights"); + b.Navigation("Tags"); }); + modelBuilder.Entity("Domain.Entities.Highlight", b => + { + b.Navigation("Rects"); + }); + modelBuilder.Entity("Domain.Entities.User", b => { b.Navigation("Books"); diff --git a/src/Infrastructure/Persistence/Repository/BookRepository.cs b/src/Infrastructure/Persistence/Repository/BookRepository.cs index c4ee1dd..667d536 100644 --- a/src/Infrastructure/Persistence/Repository/BookRepository.cs +++ b/src/Infrastructure/Persistence/Repository/BookRepository.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using Application.Interfaces.Repositories; using Domain.Entities; using Microsoft.EntityFrameworkCore; @@ -24,6 +23,13 @@ public async Task SaveChangesAsync() public async Task LoadRelationShipsAsync(Book book) { await _context.Entry(book).Collection(p => p.Tags).LoadAsync(); + await _context.Entry(book).Collection(p => p.Highlights).LoadAsync(); + + // Load the RectFs from the loaded highlights as well + foreach (var highlight in book.Highlights) + { + await _context.Entry(highlight).Collection(p => p.Rects).LoadAsync(); + } } public async Task LoadRelationShipsAsync(IEnumerable books) diff --git a/src/Infrastructure/Persistence/Repository/HighlightRepository.cs b/src/Infrastructure/Persistence/Repository/HighlightRepository.cs new file mode 100644 index 0000000..8b274f2 --- /dev/null +++ b/src/Infrastructure/Persistence/Repository/HighlightRepository.cs @@ -0,0 +1,39 @@ +using Application.Interfaces.Repositories; +using Domain.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Persistence.Repository; + +public class HighlightRepository : IHighlightRepository +{ + private readonly DataContext _context; + + + public HighlightRepository(DataContext context) + { + _context = context; + } + + + public async Task SaveChangesAsync() + { + return await _context.SaveChangesAsync(); + } + + public void Add(Highlight highlight) + { + _context.Highlights.Add(highlight); + } + + public void Delete(Highlight highlight) + { + _context.Highlights.Remove(highlight); + } + + public async Task GetAsync(Guid bookId, Guid highlightGuid) + { + return await _context.Highlights.SingleOrDefaultAsync(highlight => + highlight.BookId == bookId && + highlight.HighlightId == highlightGuid); + } +} \ No newline at end of file diff --git a/src/Presentation/Controllers/HighlightController.cs b/src/Presentation/Controllers/HighlightController.cs new file mode 100644 index 0000000..cc8c6b7 --- /dev/null +++ b/src/Presentation/Controllers/HighlightController.cs @@ -0,0 +1,57 @@ +using Application.Common.DTOs.Highlights; +using Application.Common.Exceptions; +using Application.Interfaces.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Presentation.Controllers; + +[Authorize] +[ApiController] +[Route("[controller]")] +public class HighlightController : ControllerBase +{ + private readonly IHighlightService _highlightService; + private readonly ILogger _logger; + + + public HighlightController(IHighlightService highlightService, + ILogger logger) + { + _highlightService = highlightService; + _logger = logger; + } + + [HttpPost("{guid:guid}")] + public async Task CreateHighlight(Guid guid, + [FromBody] HighlightInDto highlightIn) + { + try + { + await _highlightService.CreateHighlightAsync(User.Identity!.Name, guid, + highlightIn); + return StatusCode(201); + } + catch (CommonErrorException e) + { + _logger.LogWarning("{ErrorMessage}", e.Message); + return StatusCode(e.Error.Status, e.Error); + } + } + + [HttpDelete("{bookGuid:guid}/{highlightGuid:guid}")] + public async Task DeleteHighlight(Guid bookGuid, Guid highlightGuid) + { + try + { + await _highlightService.DeleteHighlightAsync(User.Identity!.Name, bookGuid, + highlightGuid); + return NoContent(); + } + catch (CommonErrorException e) + { + _logger.LogWarning("{ErrorMessage}", e.Message); + return StatusCode(e.Error.Status, e.Error); + } + } +} \ No newline at end of file diff --git a/src/Presentation/DependencyInjection.cs b/src/Presentation/DependencyInjection.cs index a01e310..8026578 100644 --- a/src/Presentation/DependencyInjection.cs +++ b/src/Presentation/DependencyInjection.cs @@ -39,6 +39,8 @@ public static IServiceCollection AddApplicationServices( services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddHostedService(); services.AddSingleton();