added validators in project

This commit is contained in:
2026-03-10 09:52:31 +01:00
parent 2e7b9e5154
commit 9f858df5f8
17 changed files with 558 additions and 188 deletions

View File

@@ -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; }
}

View File

@@ -12,7 +12,13 @@ public class EntityToDtoMappings : Profile
{
public EntityToDtoMappings()
{
CreateMap<Book, GetBookDto>();
CreateMap<Book, GetBookDto>()
.ForMember(
dest => dest.AuthorFullName,
opt =>
opt.MapFrom(src =>
src.Author!.FirstName + " " + src.Author.LastName)
);
CreateMap<Book, GetBookDetailsDto>();
CreateMap<Author, GetAuthorDto>();

View File

@@ -0,0 +1,45 @@
using BookHive.DTO.Author;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Authors;
public class CreateAuthorDtoValidator : Validator<CreateAuthorDto>
{
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.");
}
}

View File

@@ -0,0 +1,23 @@
using BookHive.DTO.Author;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Authors;
public class GetAuthorDtoValidator : Validator<GetAuthorDto>
{
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");
}
}

View File

@@ -0,0 +1,49 @@
using BookHive.DTO.Author;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Authors;
public class UpdateAuthorDtoValidator : Validator<UpdateAuthorDto>
{
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.");
}
}

View File

@@ -0,0 +1,46 @@
using BookHive.DTO.Book;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Books;
public class CreateBookDtoValidator : Validator<CreateBookDto>
{
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("LISBN est obligatoire.")
.Matches(@"^\d{13}$")
.WithMessage("LISBN 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("Lidentifiant de lauteur 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.");
});
}
}

View File

@@ -0,0 +1,28 @@
using BookHive.DTO.Book;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Books;
public class GetBookDtoValidator : Validator<GetBookDto>
{
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("LISBN est obligatoire.")
.Matches(@"^\d{13}$")
.WithMessage("LISBN doit contenir exactement 13 chiffres.");
RuleFor(x => x.AuthorFullName)
.NotEmpty()
.WithMessage("AuthorFullName is required");
}
}

View File

@@ -0,0 +1,50 @@
using BookHive.DTO.Book;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Books;
public class UpdateBookDtoValidator : Validator<UpdateBookDto>
{
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("LISBN est obligatoire.")
.Matches(@"^\d{13}$")
.WithMessage("LISBN 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("Lidentifiant de lauteur 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.");
});
}
}

View File

@@ -0,0 +1,28 @@
using FluentValidation;
namespace BookHive.Validators;
public static class CustomValidators
{
public static IRuleBuilderOptions<T, string>
IsValidIsbn13<T>(this IRuleBuilder<T, string> ruleBuilder)
{
return ruleBuilder
.Matches(@"^\d{13}$")
.WithMessage("LISBN 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.");
}
}

View File

@@ -0,0 +1,31 @@
using BookHive.DTO.Loan;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Loans;
public class CreateLoanDtoValidator : Validator<CreateLoanDto>
{
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.");;
}
}

View File

@@ -0,0 +1,15 @@
using BookHive.Endpoints.Loans;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Loans;
public class PatchLoanDtoValidator : Validator<PatchReturnLoanRequest>
{
public PatchLoanDtoValidator()
{
RuleFor(x => x.Id)
.GreaterThan(0)
.WithMessage("Id is invalid");
}
}

View File

@@ -0,0 +1,42 @@
using BookHive.DTO.Loan;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Loans;
public class UpdateLoanDtoValidator : Validator<UpdateLoanDto>
{
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.");
});
}
}

View File

@@ -0,0 +1,41 @@
using BookHive.DTO.Member;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Members;
public class CreateMemberDtoValidator : Validator<CreateMemberDto>
{
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.");
}
}

View File

@@ -0,0 +1,45 @@
using BookHive.DTO.Member;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Members;
public class UpdateMemberDtoValidator : Validator<UpdateMemberDto>
{
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.");
}
}

View File

@@ -0,0 +1,30 @@
using BookHive.DTO.Review;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Reviews;
public class CreateReviewDtoValidator : Validator<CreateReviewDto>
{
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.");
});
}
}

View File

@@ -0,0 +1,34 @@
using BookHive.DTO.Review;
using FastEndpoints;
using FluentValidation;
namespace BookHive.Validators.Reviews;
public class UpdateReviewDtoValidator : Validator<UpdateReviewDto>
{
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.");
});
}
}