diff --git a/.idea/.idea.BookHive/.idea/workspace.xml b/.idea/.idea.BookHive/.idea/workspace.xml
index 3c1d6e7..d4cc24f 100644
--- a/.idea/.idea.BookHive/.idea/workspace.xml
+++ b/.idea/.idea.BookHive/.idea/workspace.xml
@@ -6,191 +6,23 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -213,6 +45,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -284,7 +130,7 @@
1773087844958
-
+
@@ -318,7 +164,15 @@
1773127149315
-
+
+
+ 1773128141686
+
+
+
+ 1773128141686
+
+
@@ -331,7 +185,8 @@
-
+
+
diff --git a/BookHive/DTO/Book/GetBookDto.cs b/BookHive/DTO/Book/GetBookDto.cs
index ad5eeec..7b8b6d0 100644
--- a/BookHive/DTO/Book/GetBookDto.cs
+++ b/BookHive/DTO/Book/GetBookDto.cs
@@ -6,4 +6,6 @@ public class GetBookDto
public string? Title { get; set; }
public DateOnly PublishedDate { get; set; }
public string? Genre { get; set; }
+ public string? Isbn { get; set; }
+ public string? AuthorFullName { get; set; }
}
\ No newline at end of file
diff --git a/BookHive/MappingProfiles/EntityToDtoMappings.cs b/BookHive/MappingProfiles/EntityToDtoMappings.cs
index c3a6226..c48653c 100644
--- a/BookHive/MappingProfiles/EntityToDtoMappings.cs
+++ b/BookHive/MappingProfiles/EntityToDtoMappings.cs
@@ -12,7 +12,13 @@ public class EntityToDtoMappings : Profile
{
public EntityToDtoMappings()
{
- CreateMap();
+ CreateMap()
+ .ForMember(
+ dest => dest.AuthorFullName,
+ opt =>
+ opt.MapFrom(src =>
+ src.Author!.FirstName + " " + src.Author.LastName)
+ );
CreateMap();
CreateMap();
diff --git a/BookHive/Validators/Authors/CreateAuthorDtoValidator.cs b/BookHive/Validators/Authors/CreateAuthorDtoValidator.cs
new file mode 100644
index 0000000..ad388db
--- /dev/null
+++ b/BookHive/Validators/Authors/CreateAuthorDtoValidator.cs
@@ -0,0 +1,45 @@
+using BookHive.DTO.Author;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Authors;
+
+public class CreateAuthorDtoValidator : Validator
+{
+ public CreateAuthorDtoValidator()
+ {
+ RuleFor(x => x.FirstName)
+ .NotEmpty()
+ .WithMessage("First name is required.")
+ .MaximumLength(100)
+ .WithMessage("First name cannot exceed 100 characters.")
+ .MinimumLength(2)
+ .WithMessage("First name must exceed 2 characters.");
+
+ RuleFor(x => x.LastName)
+ .NotEmpty()
+ .WithMessage("Last name is required.")
+ .MaximumLength(100)
+ .WithMessage("Last name cannot exceed 100 characters.")
+ .MinimumLength(2)
+ .WithMessage("Last name must exceed 2 characters.");
+
+ RuleFor(x => x.Biography)
+ .MaximumLength(2000)
+ .WithMessage("Biography cannot exceed 2000 characters.")
+ .MinimumLength(2)
+ .WithMessage("Biography must exceed 2 characters.");
+
+ RuleFor(x => x.BirthDate)
+ .NotEmpty()
+ .WithMessage("Birth date is required.")
+ .LessThan(DateOnly.FromDateTime(DateTime.Now))
+ .WithMessage("Birth date cannot be in the future.");
+
+ RuleFor(x => x.Nationality)
+ .NotEmpty()
+ .WithMessage("Nationality is required.")
+ .MaximumLength(60)
+ .WithMessage("Nationality cannot exceed 60 characters.");
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Authors/GetAuthorDtoValidator.cs b/BookHive/Validators/Authors/GetAuthorDtoValidator.cs
new file mode 100644
index 0000000..46273b9
--- /dev/null
+++ b/BookHive/Validators/Authors/GetAuthorDtoValidator.cs
@@ -0,0 +1,23 @@
+using BookHive.DTO.Author;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Authors;
+
+public class GetAuthorDtoValidator : Validator
+{
+ public GetAuthorDtoValidator()
+ {
+ RuleFor(x => x.Id)
+ .GreaterThan(0)
+ .WithMessage("Id is invalid");
+
+ RuleFor(x => x.FirstName)
+ .NotEmpty()
+ .WithMessage("First name is required");
+
+ RuleFor(x => x.LastName)
+ .NotEmpty()
+ .WithMessage("Last name is required");
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Authors/UpdateAuthorDtoValidator.cs b/BookHive/Validators/Authors/UpdateAuthorDtoValidator.cs
new file mode 100644
index 0000000..dfe405e
--- /dev/null
+++ b/BookHive/Validators/Authors/UpdateAuthorDtoValidator.cs
@@ -0,0 +1,49 @@
+using BookHive.DTO.Author;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Authors;
+
+public class UpdateAuthorDtoValidator : Validator
+{
+ public UpdateAuthorDtoValidator()
+ {
+ RuleFor(x => x.Id)
+ .GreaterThan(0)
+ .WithMessage("Id is invalid.");
+
+ RuleFor(x => x.FirstName)
+ .NotEmpty()
+ .WithMessage("First name is required.")
+ .MaximumLength(100)
+ .WithMessage("First name cannot exceed 100 characters.")
+ .MinimumLength(2)
+ .WithMessage("First name must exceed 2 characters.");
+
+ RuleFor(x => x.LastName)
+ .NotEmpty()
+ .WithMessage("Last name is required.")
+ .MaximumLength(100)
+ .WithMessage("Last name cannot exceed 100 characters.")
+ .MinimumLength(2)
+ .WithMessage("Last name must exceed 2 characters.");
+
+ RuleFor(x => x.Biography)
+ .MaximumLength(2000)
+ .WithMessage("Biography cannot exceed 2000 characters.")
+ .MinimumLength(2)
+ .WithMessage("Biography must exceed 2 characters.");
+
+ RuleFor(x => x.BirthDate)
+ .NotEmpty()
+ .WithMessage("Birth date is required.")
+ .LessThan(DateOnly.FromDateTime(DateTime.Now))
+ .WithMessage("Birth date cannot be in the future.");
+
+ RuleFor(x => x.Nationality)
+ .NotEmpty()
+ .WithMessage("Nationality is required.")
+ .MaximumLength(60)
+ .WithMessage("Nationality cannot exceed 60 characters.");
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Books/CreateBookDtoValidator.cs b/BookHive/Validators/Books/CreateBookDtoValidator.cs
new file mode 100644
index 0000000..92dcdeb
--- /dev/null
+++ b/BookHive/Validators/Books/CreateBookDtoValidator.cs
@@ -0,0 +1,46 @@
+using BookHive.DTO.Book;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Books;
+
+public class CreateBookDtoValidator : Validator
+{
+ public CreateBookDtoValidator()
+ {
+ RuleFor(x => x.Title)
+ .NotEmpty().WithMessage("Le titre est obligatoire.")
+ .MaximumLength(200)
+ .WithMessage("Le titre ne peut pas dépasser 200 caractères.");
+
+ RuleFor(x => x.Isbn)
+ .NotEmpty().WithMessage("L’ISBN est obligatoire.")
+ .Matches(@"^\d{13}$")
+ .WithMessage("L’ISBN doit contenir exactement 13 chiffres.");
+
+ RuleFor(x => x.PageCount)
+ .GreaterThan(0)
+ .WithMessage("Le nombre de pages doit être supérieur à 0.");
+
+ RuleFor(x => x.PublishedDate)
+ .NotEmpty().WithMessage("La date de publication est obligatoire.")
+ .Must(d => d <= DateOnly.FromDateTime(DateTime.Now))
+ .WithMessage("La date de publication ne peut pas être dans le futur.");
+
+ RuleFor(x => x.Genre)
+ .NotEmpty()
+ .WithMessage("Le genre est obligatoire.")
+ .MaximumLength(50)
+ .WithMessage("Le genre ne peut pas dépasser 50 caractères.");
+
+ RuleFor(x => x.AuthorId)
+ .GreaterThan(0)
+ .WithMessage("L’identifiant de l’auteur est invalide.");
+
+ When(x => x.Summary != null, () =>
+ {
+ RuleFor(x => x.Summary)
+ .MaximumLength(3000).WithMessage("Le résumé ne peut pas dépasser 3000 caractères.");
+ });
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Books/GetBookDtoValidator.cs b/BookHive/Validators/Books/GetBookDtoValidator.cs
new file mode 100644
index 0000000..64aae50
--- /dev/null
+++ b/BookHive/Validators/Books/GetBookDtoValidator.cs
@@ -0,0 +1,28 @@
+using BookHive.DTO.Book;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Books;
+
+public class GetBookDtoValidator : Validator
+{
+ public GetBookDtoValidator()
+ {
+ RuleFor(x => x.Id)
+ .GreaterThan(0)
+ .WithMessage("Id is invalid");
+
+ RuleFor(x => x.Title)
+ .NotEmpty()
+ .WithMessage("Title is required");
+
+ RuleFor(x => x.Isbn)
+ .NotEmpty().WithMessage("L’ISBN est obligatoire.")
+ .Matches(@"^\d{13}$")
+ .WithMessage("L’ISBN doit contenir exactement 13 chiffres.");
+
+ RuleFor(x => x.AuthorFullName)
+ .NotEmpty()
+ .WithMessage("AuthorFullName is required");
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Books/UpdateBookDtoValidator.cs b/BookHive/Validators/Books/UpdateBookDtoValidator.cs
new file mode 100644
index 0000000..1d18483
--- /dev/null
+++ b/BookHive/Validators/Books/UpdateBookDtoValidator.cs
@@ -0,0 +1,50 @@
+using BookHive.DTO.Book;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Books;
+
+public class UpdateBookDtoValidator : Validator
+{
+ public UpdateBookDtoValidator()
+ {
+ RuleFor(x => x.Id)
+ .GreaterThan(0)
+ .WithMessage("Id is invalid.");
+
+ RuleFor(x => x.Title)
+ .NotEmpty().WithMessage("Le titre est obligatoire.")
+ .MaximumLength(200)
+ .WithMessage("Le titre ne peut pas dépasser 200 caractères.");
+
+ RuleFor(x => x.Isbn)
+ .NotEmpty().WithMessage("L’ISBN est obligatoire.")
+ .Matches(@"^\d{13}$")
+ .WithMessage("L’ISBN doit contenir exactement 13 chiffres.");
+
+ RuleFor(x => x.PageCount)
+ .GreaterThan(0)
+ .WithMessage("Le nombre de pages doit être supérieur à 0.");
+
+ RuleFor(x => x.PublishedDate)
+ .NotEmpty().WithMessage("La date de publication est obligatoire.")
+ .Must(d => d <= DateOnly.FromDateTime(DateTime.Now))
+ .WithMessage("La date de publication ne peut pas être dans le futur.");
+
+ RuleFor(x => x.Genre)
+ .NotEmpty()
+ .WithMessage("Le genre est obligatoire.")
+ .MaximumLength(50)
+ .WithMessage("Le genre ne peut pas dépasser 50 caractères.");
+
+ RuleFor(x => x.AuthorId)
+ .GreaterThan(0)
+ .WithMessage("L’identifiant de l’auteur est invalide.");
+
+ When(x => x.Summary != null, () =>
+ {
+ RuleFor(x => x.Summary)
+ .MaximumLength(3000).WithMessage("Le résumé ne peut pas dépasser 3000 caractères.");
+ });
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/CustomValidators.cs b/BookHive/Validators/CustomValidators.cs
new file mode 100644
index 0000000..5b01e8f
--- /dev/null
+++ b/BookHive/Validators/CustomValidators.cs
@@ -0,0 +1,28 @@
+using FluentValidation;
+
+namespace BookHive.Validators;
+
+public static class CustomValidators
+{
+ public static IRuleBuilderOptions
+ IsValidIsbn13(this IRuleBuilder ruleBuilder)
+ {
+ return ruleBuilder
+ .Matches(@"^\d{13}$")
+ .WithMessage("L’ISBN doit contenir 13 chiffres.")
+ .Must(isbn =>
+ {
+ if (string.IsNullOrEmpty(isbn) || isbn.Length != 13)
+ return false;
+ var sum = 0;
+ for (int i = 0; i < 12; i++)
+ {
+ var digit = isbn[i] - '0';
+ sum += (i % 2 == 0) ? digit : digit * 3;
+ }
+ var checkDigit = (10 - (sum % 10)) % 10;
+ return checkDigit == (isbn[12] - '0');
+ })
+ .WithMessage("Le chiffre de contrôle ISBN est invalide.");
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Loans/CreateLoanDtoValidator.cs b/BookHive/Validators/Loans/CreateLoanDtoValidator.cs
new file mode 100644
index 0000000..7cfd56a
--- /dev/null
+++ b/BookHive/Validators/Loans/CreateLoanDtoValidator.cs
@@ -0,0 +1,31 @@
+using BookHive.DTO.Loan;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Loans;
+
+public class CreateLoanDtoValidator : Validator
+{
+ public CreateLoanDtoValidator()
+ {
+ RuleFor(x => x.BookId)
+ .GreaterThan(0)
+ .WithMessage("Invalid book id.");
+
+ RuleFor(x => x.MemberId)
+ .GreaterThan(0)
+ .WithMessage("Invalid member id.");
+
+ RuleFor(x => x.LoanDate)
+ .NotEmpty()
+ .WithMessage("Date is required.");
+
+ RuleFor(x => x.DueDate)
+ .NotEmpty()
+ .WithMessage("Due date is required.")
+ .GreaterThan(d => d.LoanDate)
+ .WithMessage("Due date must be in the future.")
+ .Must((loan, dueDate) => dueDate <= loan.LoanDate.AddDays(30))
+ .WithMessage("Due date must be in the future.");;
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Loans/PatchLoanDtoValidator.cs b/BookHive/Validators/Loans/PatchLoanDtoValidator.cs
new file mode 100644
index 0000000..589e56e
--- /dev/null
+++ b/BookHive/Validators/Loans/PatchLoanDtoValidator.cs
@@ -0,0 +1,15 @@
+using BookHive.Endpoints.Loans;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Loans;
+
+public class PatchLoanDtoValidator : Validator
+{
+ public PatchLoanDtoValidator()
+ {
+ RuleFor(x => x.Id)
+ .GreaterThan(0)
+ .WithMessage("Id is invalid");
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Loans/UpdateLoanDtoValidator.cs b/BookHive/Validators/Loans/UpdateLoanDtoValidator.cs
new file mode 100644
index 0000000..3fc93f8
--- /dev/null
+++ b/BookHive/Validators/Loans/UpdateLoanDtoValidator.cs
@@ -0,0 +1,42 @@
+using BookHive.DTO.Loan;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Loans;
+
+public class UpdateLoanDtoValidator : Validator
+{
+ public UpdateLoanDtoValidator()
+ {
+ RuleFor(x => x.Id)
+ .GreaterThan(0)
+ .WithMessage("Id is invalid.");
+
+ RuleFor(x => x.BookId)
+ .GreaterThan(0)
+ .WithMessage("Invalid book id.");
+
+ RuleFor(x => x.MemberId)
+ .GreaterThan(0)
+ .WithMessage("Invalid member id.");
+
+ RuleFor(x => x.LoanDate)
+ .NotEmpty()
+ .WithMessage("Date is required.");
+
+ RuleFor(x => x.DueDate)
+ .NotEmpty()
+ .WithMessage("Due date is required.")
+ .GreaterThan(d => d.LoanDate)
+ .WithMessage("Due date must be in the future.")
+ .Must((loan, dueDate) => dueDate <= loan.LoanDate.AddDays(30))
+ .WithMessage("Due date must be in the future.");
+
+ When(x => x.ReturnDate != null, () =>
+ {
+ RuleFor(x => x.ReturnDate)
+ .GreaterThan(d => d.LoanDate)
+ .WithMessage("Return date must be in the future.");
+ });
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Members/CreateMemberDtoValidator.cs b/BookHive/Validators/Members/CreateMemberDtoValidator.cs
new file mode 100644
index 0000000..eb89b14
--- /dev/null
+++ b/BookHive/Validators/Members/CreateMemberDtoValidator.cs
@@ -0,0 +1,41 @@
+using BookHive.DTO.Member;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Members;
+
+public class CreateMemberDtoValidator : Validator
+{
+ public CreateMemberDtoValidator()
+ {
+ RuleFor(x => x.Email)
+ .NotEmpty()
+ .WithMessage("Email is required.")
+ .EmailAddress()
+ .WithMessage("Email is invalid.")
+ .MaximumLength(255)
+ .WithMessage("Email cannot be longer than 255 characters.");
+
+ RuleFor(x => x.FirstName)
+ .NotEmpty()
+ .WithMessage("First name is required.")
+ .MaximumLength(100)
+ .WithMessage("First name cannot be longer than 100 characters.")
+ .MinimumLength(2)
+ .WithMessage("First name cannot be shorter than 2 characters.");
+
+ RuleFor(x => x.LastName)
+ .NotEmpty()
+ .WithMessage("Last name is required.")
+ .MaximumLength(100)
+ .WithMessage("Last name cannot be longer than 100 characters.")
+ .MinimumLength(2)
+ .WithMessage("Last name cannot be shorter than 2 characters.");
+
+ RuleFor(x => x.MembershipDate)
+ .NotEmpty()
+ .WithMessage("Membership date is required.")
+ .Must(d => d <= DateOnly.FromDateTime(DateTime.Now))
+ .WithMessage("Membership date cannot be in the future.");
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Members/UpdateMemberDtoValidator.cs b/BookHive/Validators/Members/UpdateMemberDtoValidator.cs
new file mode 100644
index 0000000..88c7370
--- /dev/null
+++ b/BookHive/Validators/Members/UpdateMemberDtoValidator.cs
@@ -0,0 +1,45 @@
+using BookHive.DTO.Member;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Members;
+
+public class UpdateMemberDtoValidator : Validator
+{
+ public UpdateMemberDtoValidator()
+ {
+ RuleFor(x => x.Id)
+ .GreaterThan(0)
+ .WithMessage("Id is invalid.");
+
+ RuleFor(x => x.Email)
+ .NotEmpty()
+ .WithMessage("Email is required.")
+ .EmailAddress()
+ .WithMessage("Email is invalid.")
+ .MaximumLength(255)
+ .WithMessage("Email cannot be longer than 255 characters.");
+
+ RuleFor(x => x.FirstName)
+ .NotEmpty()
+ .WithMessage("First name is required.")
+ .MaximumLength(100)
+ .WithMessage("First name cannot be longer than 100 characters.")
+ .MinimumLength(2)
+ .WithMessage("First name cannot be shorter than 2 characters.");
+
+ RuleFor(x => x.LastName)
+ .NotEmpty()
+ .WithMessage("Last name is required.")
+ .MaximumLength(100)
+ .WithMessage("Last name cannot be longer than 100 characters.")
+ .MinimumLength(2)
+ .WithMessage("Last name cannot be shorter than 2 characters.");
+
+ RuleFor(x => x.MembershipDate)
+ .NotEmpty()
+ .WithMessage("Membership date is required.")
+ .Must(d => d <= DateOnly.FromDateTime(DateTime.Now))
+ .WithMessage("Membership date cannot be in the future.");
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Reviews/CreateReviewDtoValidator.cs b/BookHive/Validators/Reviews/CreateReviewDtoValidator.cs
new file mode 100644
index 0000000..4a2caca
--- /dev/null
+++ b/BookHive/Validators/Reviews/CreateReviewDtoValidator.cs
@@ -0,0 +1,30 @@
+using BookHive.DTO.Review;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Reviews;
+
+public class CreateReviewDtoValidator : Validator
+{
+ public CreateReviewDtoValidator()
+ {
+ RuleFor(x => x.MemberId)
+ .GreaterThan(0)
+ .WithMessage("Member id is invalid.");
+
+ RuleFor(x => x.BookId)
+ .GreaterThan(0)
+ .WithMessage("Book id is invalid.");
+
+ RuleFor(x => x.Rating)
+ .InclusiveBetween(1, 5)
+ .WithMessage("Rating must be between 1 and 5.");
+
+ When(x => x.Comment is not null, () =>
+ {
+ RuleFor(x => x.Comment)
+ .MaximumLength(3000)
+ .WithMessage("Comment cannot exceed 3000 characters.");
+ });
+ }
+}
\ No newline at end of file
diff --git a/BookHive/Validators/Reviews/UpdateReviewDtoValidator.cs b/BookHive/Validators/Reviews/UpdateReviewDtoValidator.cs
new file mode 100644
index 0000000..c1d0980
--- /dev/null
+++ b/BookHive/Validators/Reviews/UpdateReviewDtoValidator.cs
@@ -0,0 +1,34 @@
+using BookHive.DTO.Review;
+using FastEndpoints;
+using FluentValidation;
+
+namespace BookHive.Validators.Reviews;
+
+public class UpdateReviewDtoValidator : Validator
+{
+ public UpdateReviewDtoValidator()
+ {
+ RuleFor(x => x.Id)
+ .GreaterThan(0)
+ .WithMessage("Id is invalid.");
+
+ RuleFor(x => x.BookId)
+ .GreaterThan(0)
+ .WithMessage("Book id is invalid.");
+
+ RuleFor(x => x.MemberId)
+ .GreaterThan(0)
+ .WithMessage("Member id is invalid.");
+
+ RuleFor(x => x.Rating)
+ .InclusiveBetween(1, 5)
+ .WithMessage("Rating must be between 1 and 5.");
+
+ When(x => x.Comment is not null, () =>
+ {
+ RuleFor(x => x.Comment)
+ .MaximumLength(3000)
+ .WithMessage("Comment cannot exceed 3000 characters.");
+ });
+ }
+}
\ No newline at end of file