diff --git a/server/StudySharp.API/Controllers/CourseController.cs b/server/StudySharp.API/Controllers/CourseController.cs index 3337ebc..51b4100 100644 --- a/server/StudySharp.API/Controllers/CourseController.cs +++ b/server/StudySharp.API/Controllers/CourseController.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using AutoMapper; using MediatR; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using StudySharp.API.Requests.Courses; using StudySharp.API.Requests.PracticalBlocks; diff --git a/server/StudySharp.ApplicationServices/Commands/AddCourseCommand.cs b/server/StudySharp.ApplicationServices/Commands/AddCourseCommand.cs index f0fd2e6..5cc2a61 100644 --- a/server/StudySharp.ApplicationServices/Commands/AddCourseCommand.cs +++ b/server/StudySharp.ApplicationServices/Commands/AddCourseCommand.cs @@ -1,10 +1,12 @@ using System.Threading; using System.Threading.Tasks; +using FluentValidation; using MediatR; using Microsoft.EntityFrameworkCore; using StudySharp.Domain.Constants; using StudySharp.Domain.General; using StudySharp.Domain.Models; +using StudySharp.Domain.ValidationRules; using StudySharp.DomainServices; namespace StudySharp.ApplicationServices.Commands @@ -15,6 +17,24 @@ public sealed class AddCourseCommand : IRequest public int TeacherId { get; set; } } + public class AddCourseCommandValidator : AbstractValidator + { + public AddCourseCommandValidator(ICourseRules rules) + { + RuleFor(_ => new { _.Name, _.TeacherId }) + .NotEmpty() + .WithMessage(string.Format(ErrorConstants.FieldIsRequired, nameof(Course.Name))) + .MustAsync((courseTeacherAndName, token) => rules.IsNameUniqueAsync(courseTeacherAndName.Name, courseTeacherAndName.TeacherId, token)) + .WithMessage(_ => string.Format(ErrorConstants.EntityAlreadyExists, nameof(Course), nameof(Course.Name), _.Name)); + + RuleFor(_ => _.TeacherId) + .NotEmpty() + .WithMessage(string.Format(ErrorConstants.FieldIsRequired, nameof(Course.TeacherId))) + .MustAsync(rules.IsTeacherIdExistAsync) + .WithMessage(_ => string.Format(ErrorConstants.EntityNotFound, nameof(Teacher), nameof(Teacher.Id), _.TeacherId)); + } + } + public sealed class AddCourseCommandHandler : IRequestHandler { private readonly StudySharpDbContext _context; @@ -26,9 +46,7 @@ public AddCourseCommandHandler(StudySharpDbContext sharpDbContext) public async Task Handle(AddCourseCommand request, CancellationToken cancellationToken) { - if (await _context.Courses.AnyAsync( - _ => _.Name.ToLower().Equals(request.Name.ToLower()) && _.TeacherId == request.TeacherId, - cancellationToken)) + if (await _context.Courses.AnyAsync(_ => _.Name.ToLower().Equals(request.Name.ToLower()) && _.TeacherId == request.TeacherId)) { return OperationResult.Fail(string.Format(ErrorConstants.EntityAlreadyExists, nameof(Course), nameof(Course.Name), request.Name)); } diff --git a/server/StudySharp.ApplicationServices/Commands/RemoveCourseByIdCommand.cs b/server/StudySharp.ApplicationServices/Commands/RemoveCourseByIdCommand.cs index 0262e8a..b77a991 100644 --- a/server/StudySharp.ApplicationServices/Commands/RemoveCourseByIdCommand.cs +++ b/server/StudySharp.ApplicationServices/Commands/RemoveCourseByIdCommand.cs @@ -1,9 +1,11 @@ using System.Threading; using System.Threading.Tasks; +using FluentValidation; using MediatR; using StudySharp.Domain.Constants; using StudySharp.Domain.General; using StudySharp.Domain.Models; +using StudySharp.Domain.ValidationRules; using StudySharp.DomainServices; namespace StudySharp.ApplicationServices.Commands @@ -13,6 +15,16 @@ public sealed class RemoveCourseByIdCommand : IRequest public int Id { get; set; } } + public class RemoveCourseByIdCommandValidator : AbstractValidator + { + public RemoveCourseByIdCommandValidator(ICourseRules rules) + { + RuleFor(_ => _.Id) + .MustAsync(rules.IsCourseIdExistAsync) + .WithMessage(_ => string.Format(ErrorConstants.EntityNotFound, nameof(Course), nameof(Course.Id), _.Id)); + } + } + public sealed class RemoveCourseByIdCommandHandler : IRequestHandler { private readonly StudySharpDbContext _context; @@ -24,12 +36,7 @@ public RemoveCourseByIdCommandHandler(StudySharpDbContext sharpDbContext) public async Task Handle(RemoveCourseByIdCommand request, CancellationToken cancellationToken) { - var course = await _context.Courses.FindAsync(request.Id, cancellationToken); - if (course == null) - { - return OperationResult.Fail(string.Format(ErrorConstants.EntityNotFound, nameof(Course), nameof(Course.Name), request.Id)); - } - + var course = await _context.Courses.FindAsync(request.Id); _context.Courses.Remove(course); await _context.SaveChangesAsync(cancellationToken); return OperationResult.Ok(); diff --git a/server/StudySharp.ApplicationServices/Commands/UpdateCourseCommand.cs b/server/StudySharp.ApplicationServices/Commands/UpdateCourseCommand.cs index 4b0d390..debd962 100644 --- a/server/StudySharp.ApplicationServices/Commands/UpdateCourseCommand.cs +++ b/server/StudySharp.ApplicationServices/Commands/UpdateCourseCommand.cs @@ -1,10 +1,12 @@ using System.Threading; using System.Threading.Tasks; +using FluentValidation; using MediatR; using Microsoft.EntityFrameworkCore; using StudySharp.Domain.Constants; using StudySharp.Domain.General; using StudySharp.Domain.Models; +using StudySharp.Domain.ValidationRules; using StudySharp.DomainServices; namespace StudySharp.ApplicationServices.Commands @@ -16,6 +18,28 @@ public class UpdateCourseCommand : IRequest public int TeacherId { get; set; } } + public partial class UpdateCourseCommandValidator : AbstractValidator + { + public UpdateCourseCommandValidator(ICourseRules rules) + { + RuleFor(_ => _.Id) + .MustAsync(rules.IsCourseIdExistAsync) + .WithMessage(_ => string.Format(ErrorConstants.EntityNotFound, nameof(Course), nameof(Course.Id), _.Id)); + + RuleFor(_ => new { _.Name, _.TeacherId }) + .NotEmpty() + .WithMessage(string.Format(ErrorConstants.FieldIsRequired, nameof(Course.Name))) + .MustAsync((courseTeacherAndName, token) => rules.IsNameUniqueAsync(courseTeacherAndName.Name, courseTeacherAndName.TeacherId, token)) + .WithMessage(_ => string.Format(ErrorConstants.EntityAlreadyExists, nameof(Course), nameof(Course.Name), _.Name)); + + RuleFor(_ => _.TeacherId) + .NotEmpty() + .WithMessage(string.Format(ErrorConstants.FieldIsRequired, nameof(Course.TeacherId))) + .MustAsync(rules.IsTeacherIdExistAsync) + .WithMessage(_ => string.Format(ErrorConstants.EntityNotFound, nameof(Teacher), nameof(Teacher.Id), _.TeacherId)); + } + } + public sealed class UpdateCourseCommandHandler : IRequestHandler { private readonly StudySharpDbContext _context; @@ -28,17 +52,6 @@ public UpdateCourseCommandHandler(StudySharpDbContext sharpDbContext) public async Task Handle(UpdateCourseCommand request, CancellationToken cancellationToken) { var course = await _context.Courses.FirstOrDefaultAsync(_ => _.Id == request.Id, cancellationToken); - if (course == null) - { - return OperationResult.Fail(string.Format(ErrorConstants.EntityNotFound, nameof(Course), nameof(Course.Id), request.Id)); - } - - var teacherExistent = await _context.Teachers.AnyAsync(_ => _.Id == request.TeacherId, cancellationToken); - if (!teacherExistent) - { - return OperationResult.Fail(string.Format(ErrorConstants.EntityNotFound, nameof(Teacher), nameof(Teacher.Id), request.TeacherId)); - } - course.Name = request.Name; course.TeacherId = request.TeacherId; diff --git a/server/StudySharp.ApplicationServices/Queries/GetCourseByIdQuery.cs b/server/StudySharp.ApplicationServices/Queries/GetCourseByIdQuery.cs index ca14897..5c06701 100644 --- a/server/StudySharp.ApplicationServices/Queries/GetCourseByIdQuery.cs +++ b/server/StudySharp.ApplicationServices/Queries/GetCourseByIdQuery.cs @@ -1,9 +1,11 @@ using System.Threading; using System.Threading.Tasks; +using FluentValidation; using MediatR; using StudySharp.Domain.Constants; using StudySharp.Domain.General; using StudySharp.Domain.Models; +using StudySharp.Domain.ValidationRules; using StudySharp.DomainServices; namespace StudySharp.ApplicationServices.Queries @@ -13,6 +15,16 @@ public sealed class GetCourseByIdQuery : IRequest> public int Id { get; set; } } + public partial class GetCourseByIdQueryValidator : AbstractValidator + { + public GetCourseByIdQueryValidator(ICourseRules rules) + { + RuleFor(_ => _.Id) + .MustAsync(rules.IsCourseIdExistAsync) + .WithMessage(_ => string.Format(ErrorConstants.EntityNotFound, nameof(Course), nameof(Course.Id), _.Id)); + } + } + public sealed class GetCourseByIdQueryHandler : IRequestHandler> { private readonly StudySharpDbContext _context; @@ -24,12 +36,7 @@ public GetCourseByIdQueryHandler(StudySharpDbContext studySharpDbContext) public async Task> Handle(GetCourseByIdQuery request, CancellationToken cancellationToken) { - var course = await _context.Courses.FindAsync(request.Id, cancellationToken); - if (course == null) - { - return OperationResult.Fail(string.Format(ErrorConstants.EntityNotFound, nameof(Course), nameof(Course.Id), request.Id)); - } - + var course = await _context.Courses.FindAsync(request.Id); return OperationResult.Ok(course); } } diff --git a/server/StudySharp.ApplicationServices/Queries/GetCoursesByTeacherIdQuery.cs b/server/StudySharp.ApplicationServices/Queries/GetCoursesByTeacherIdQuery.cs index 8387543..6292319 100644 --- a/server/StudySharp.ApplicationServices/Queries/GetCoursesByTeacherIdQuery.cs +++ b/server/StudySharp.ApplicationServices/Queries/GetCoursesByTeacherIdQuery.cs @@ -2,11 +2,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using FluentValidation; using MediatR; using Microsoft.EntityFrameworkCore; using StudySharp.Domain.Constants; using StudySharp.Domain.General; using StudySharp.Domain.Models; +using StudySharp.Domain.ValidationRules; using StudySharp.DomainServices; namespace StudySharp.ApplicationServices.Queries @@ -16,6 +18,16 @@ public sealed class GetCoursesByTeacherIdQuery : IRequest + { + public GetCoursesByTeacherIdQueryValidator(ICourseRules rules) + { + RuleFor(_ => _.TeacherId) + .MustAsync(rules.IsTeacherIdExistAsync) + .WithMessage(_ => string.Format(ErrorConstants.EntityNotFound, nameof(Teacher), nameof(Teacher.Id), _.TeacherId)); + } + } + public sealed class GetCoursesByTeacherIdQueryHandler : IRequestHandler>> { private readonly StudySharpDbContext _context; @@ -27,12 +39,6 @@ public GetCoursesByTeacherIdQueryHandler(StudySharpDbContext studySharpDbContext public async Task>> Handle(GetCoursesByTeacherIdQuery request, CancellationToken cancellationToken) { - var isTeacherExistent = await _context.Teachers.AnyAsync(_ => _.Id == request.TeacherId, cancellationToken); - if (!isTeacherExistent) - { - return OperationResult.Fail>(string.Format(ErrorConstants.EntityNotFound, nameof(Teacher), nameof(Teacher.Id), request.TeacherId)); - } - var courses = await _context.Courses.Where(_ => _.TeacherId == request.TeacherId).ToListAsync(cancellationToken); return OperationResult.Ok(courses); } diff --git a/server/StudySharp.ApplicationServices/ValidationRules/CourseRules.cs b/server/StudySharp.ApplicationServices/ValidationRules/CourseRules.cs new file mode 100644 index 0000000..353b64c --- /dev/null +++ b/server/StudySharp.ApplicationServices/ValidationRules/CourseRules.cs @@ -0,0 +1,33 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using StudySharp.Domain.ValidationRules; +using StudySharp.DomainServices; + +namespace StudySharp.ApplicationServices.ValidationRules +{ + public class CourseRules : ICourseRules + { + private readonly StudySharpDbContext _context; + + public CourseRules(StudySharpDbContext context) + { + _context = context; + } + + public async Task IsNameUniqueAsync(string name, int teacherId, CancellationToken cancellationToken) + { + return !await _context.Courses.AnyAsync(_ => _.Name == name && _.TeacherId == teacherId, cancellationToken); + } + + public async Task IsCourseIdExistAsync(int id, CancellationToken cancellationToken) + { + return await _context.Courses.AnyAsync(_ => _.Id == id, cancellationToken); + } + + public async Task IsTeacherIdExistAsync(int teacherId, CancellationToken cancellationToken) + { + return await _context.Teachers.AnyAsync(_ => _.Id == teacherId, cancellationToken); + } + } +} \ No newline at end of file diff --git a/server/StudySharp.Domain/ValidationRules/ICourseRules.cs b/server/StudySharp.Domain/ValidationRules/ICourseRules.cs new file mode 100644 index 0000000..f9bc374 --- /dev/null +++ b/server/StudySharp.Domain/ValidationRules/ICourseRules.cs @@ -0,0 +1,12 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace StudySharp.Domain.ValidationRules +{ + public interface ICourseRules : IValidationRule + { + Task IsCourseIdExistAsync(int id, CancellationToken cancellationToken); + Task IsTeacherIdExistAsync(int teacherId, CancellationToken cancellationToken); + Task IsNameUniqueAsync(string name, int teacherId, CancellationToken cancellationToken); + } +} \ No newline at end of file