Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7c42c1e
updated database seeder
ZhmudAnastasiia Jul 21, 2025
d9f6798
fix integrational tests
MotrukOleg Jul 29, 2025
dd10411
move to in memory database and added possibility to add custom seeder…
MotrukOleg Jul 29, 2025
53e437c
Added InMemoryDbContext, fixed integration tests
daniilshevch Jul 30, 2025
1455f8d
fix naming
MotrukOleg Jul 30, 2025
6ec55bd
fix merge conf
MotrukOleg Jul 30, 2025
4fac8ca
created models
OlyaMraka Jul 30, 2025
e9a38ec
Added commands, handler, queries, tests
OlyaMraka Jul 31, 2025
2942c71
migration
OlyaMraka Jul 31, 2025
4ed27c4
resolve merge conf
MotrukOleg Jul 31, 2025
a2d583c
fix image integration tests
MotrukOleg Jul 31, 2025
299e967
Merge branch 'feature/issue-168' into feature/issue-65-final
OlyaMraka Aug 1, 2025
c9ec121
Added tests
OlyaMraka Aug 2, 2025
69f167c
Added GetByFilters, fixed bugs
OlyaMraka Aug 6, 2025
5db32ae
little fixes
OlyaMraka Aug 11, 2025
c21d3bc
fix integration tests
MotrukOleg Aug 11, 2025
5f358bd
minor fixes
OlyaMraka Aug 12, 2025
3d403f1
fixed SonarQube issues
OlyaMraka Aug 16, 2025
4ae81ff
Merge branch 'release/1.0.0' into feature/issue-65-final
OlyaMraka Aug 16, 2025
cba9772
fixed merge conflicts
OlyaMraka Aug 16, 2025
7b84c41
minor fixes
OlyaMraka Aug 16, 2025
cd83347
fixed SonarQube issues
OlyaMraka Aug 16, 2025
c8ee5e0
SonarQube fixies
OlyaMraka Aug 16, 2025
74acdde
moved id from body to route
OlyaMraka Aug 17, 2025
7125cc0
Merge branch 'release/1.0.0' into feature/issue-65-final
OlyaMraka Aug 21, 2025
6494676
minor fixes
OlyaMraka Aug 21, 2025
2249125
fixed comments
Kitukl Aug 27, 2025
796deb5
minor fixes
Kitukl Aug 27, 2025
20c250e
fixed comments
Kitukl Aug 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using FluentResults;
using MediatR;
using VictoryCenter.BLL.DTOs.ProgramCategories;

namespace VictoryCenter.BLL.Commands.ProgramCategories.Create;

public record CreateProgramCategoryCommand(CreateProgramCategoryDto programCategoryDto)
: IRequest<Result<ProgramCategoryDto>>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using MediatR;
using AutoMapper;
using FluentResults;
using FluentValidation;
using VictoryCenter.BLL.Constants;
using VictoryCenter.BLL.DTOs.ProgramCategories;
using VictoryCenter.DAL.Repositories.Interfaces.Base;

namespace VictoryCenter.BLL.Commands.ProgramCategories.Create;

public class CreateProgramCategoryHandler : IRequestHandler<CreateProgramCategoryCommand, Result<ProgramCategoryDto>>
{
private readonly IMapper _mapper;
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly IValidator<CreateProgramCategoryCommand> _validator;

public CreateProgramCategoryHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper, IValidator<CreateProgramCategoryCommand> validator)
{
_mapper = mapper;
_repositoryWrapper = repositoryWrapper;
_validator = validator;
}

public async Task<Result<ProgramCategoryDto>> Handle(CreateProgramCategoryCommand request, CancellationToken cancellationToken)
{
try
{
await _validator.ValidateAndThrowAsync(request, cancellationToken);

var entity = _mapper.Map<DAL.Entities.ProgramCategory>(request.programCategoryDto);
entity.CreatedAt = DateTime.UtcNow;
await _repositoryWrapper.ProgramCategoriesRepository.CreateAsync(entity);

if (await _repositoryWrapper.SaveChangesAsync() > 0)
{
var responseDto = _mapper.Map<ProgramCategoryDto>(entity);
return Result.Ok(responseDto);
}

return Result.Fail<ProgramCategoryDto>(ProgramCategoryConstants.FailedToCreateCategory);
}
catch (ValidationException ex)
{
return Result.Fail<ProgramCategoryDto>(ex.Message);
Comment thread
Kitukl marked this conversation as resolved.
Outdated
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using MediatR;
using FluentResults;

namespace VictoryCenter.BLL.Commands.ProgramCategories.Delete;

public record DeleteProgramCategoryCommand(long id) : IRequest<Result<long>>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using MediatR;
using FluentResults;
using Microsoft.EntityFrameworkCore;
using VictoryCenter.BLL.Constants;
using VictoryCenter.DAL.Repositories.Options;
using VictoryCenter.DAL.Repositories.Interfaces.Base;

namespace VictoryCenter.BLL.Commands.ProgramCategories.Delete;

public class DeleteProgramCategoryHandler : IRequestHandler<DeleteProgramCategoryCommand, Result<long>>
{
private readonly IRepositoryWrapper _repositoryWrapper;

public DeleteProgramCategoryHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}

public async Task<Result<long>> Handle(DeleteProgramCategoryCommand request, CancellationToken cancellationToken)
Comment thread
Kitukl marked this conversation as resolved.
{
var queryOptions = new QueryOptions<DAL.Entities.ProgramCategory>
{
Filter = programCategory => programCategory.Id == request.id,
Include = programCategory => programCategory
.Include(p => p.Programs)
};

var entityToDelete = await _repositoryWrapper.ProgramCategoriesRepository
.GetFirstOrDefaultAsync(queryOptions);

if (entityToDelete is null)
{
return Result.Fail<long>(ErrorMessagesConstants
.NotFound(request.id, typeof(DAL.Entities.ProgramCategory)));
}

if (entityToDelete.Programs.Count != 0)
{
return Result.Fail(ProgramCategoryConstants.CantDeleteProgramCategoryWhileAssociatedWithAnyProgram);
}

_repositoryWrapper.ProgramCategoriesRepository.Delete(entityToDelete);

if (await _repositoryWrapper.SaveChangesAsync() > 0)
{
return Result.Ok(entityToDelete.Id);
}

return Result.Fail(ProgramCategoryConstants.FailedToDeleteCategory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using FluentResults;
using MediatR;
using VictoryCenter.BLL.DTOs.ProgramCategories;

namespace VictoryCenter.BLL.Commands.ProgramCategories.Update;

public record UpdateProgramCategoryCommand(UpdateProgramCategoryDto updateProgramCategoryDto)
: IRequest<Result<ProgramCategoryDto>>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using MediatR;
using AutoMapper;
using FluentResults;
using FluentValidation;
using VictoryCenter.BLL.Constants;
using VictoryCenter.BLL.DTOs.ProgramCategories;
using VictoryCenter.DAL.Repositories.Options;
using VictoryCenter.DAL.Repositories.Interfaces.Base;

namespace VictoryCenter.BLL.Commands.ProgramCategories.Update;

public class UpdateProgramCategoryHandler : IRequestHandler<UpdateProgramCategoryCommand, Result<ProgramCategoryDto>>
{
private readonly IMapper _mapper;
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly IValidator<UpdateProgramCategoryCommand> _validator;

public UpdateProgramCategoryHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper, IValidator<UpdateProgramCategoryCommand> validator)
{
_mapper = mapper;
_repositoryWrapper = repositoryWrapper;
_validator = validator;
}

public async Task<Result<ProgramCategoryDto>> Handle(UpdateProgramCategoryCommand request, CancellationToken cancellationToken)
{
try
{
await _validator.ValidateAndThrowAsync(request, cancellationToken);

var programCategoryEntity = await _repositoryWrapper.ProgramCategoriesRepository
.GetFirstOrDefaultAsync(new QueryOptions<DAL.Entities.ProgramCategory>
{
Filter = programCategory => programCategory.Id == request.updateProgramCategoryDto.Id
});

if (programCategoryEntity is null)
{
return Result.Fail<ProgramCategoryDto>(ErrorMessagesConstants
.NotFound(request.updateProgramCategoryDto.Id, typeof(DAL.Entities.ProgramCategory)));
}

var entityToUpdate = _mapper.Map(request.updateProgramCategoryDto, programCategoryEntity);
entityToUpdate.CreatedAt = programCategoryEntity.CreatedAt;

_repositoryWrapper.ProgramCategoriesRepository.Update(entityToUpdate);

if (await _repositoryWrapper.SaveChangesAsync() > 0)
{
var responseDto = _mapper.Map<ProgramCategoryDto>(entityToUpdate);
return Result.Ok(responseDto);
}

return Result.Fail<ProgramCategoryDto>(ProgramCategoryConstants.FailedToUpdateCategory);
}
catch (ValidationException ex)
{
return Result.Fail<ProgramCategoryDto>(ex.Message);
Comment thread
Kitukl marked this conversation as resolved.
Outdated
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using MediatR;
using FluentResults;
using VictoryCenter.BLL.DTOs.Programs;

namespace VictoryCenter.BLL.Commands.Programs.Create;

public record CreateProgramCommand(CreateProgramDto createProgramDto) : IRequest<Result<ProgramDto>>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using MediatR;
using AutoMapper;
using FluentResults;
using FluentValidation;
using VictoryCenter.BLL.Constants;
using VictoryCenter.BLL.DTOs.Programs;
using VictoryCenter.BLL.Interfaces.BlobStorage;
using VictoryCenter.DAL.Entities;
using VictoryCenter.DAL.Repositories.Interfaces.Base;
using VictoryCenter.DAL.Repositories.Options;

namespace VictoryCenter.BLL.Commands.Programs.Create;

public class CreateProgramHandler : IRequestHandler<CreateProgramCommand, Result<ProgramDto>>
{
private readonly IMapper _mapper;
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly IValidator<CreateProgramCommand> _validator;
private readonly IBlobService _blobService;

public CreateProgramHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper, IValidator<CreateProgramCommand> validator, IBlobService blobService)
{
_mapper = mapper;
_repositoryWrapper = repositoryWrapper;
_validator = validator;
_blobService = blobService;
}

public async Task<Result<ProgramDto>> Handle(CreateProgramCommand request, CancellationToken cancellationToken)
{
try
{
await _validator.ValidateAndThrowAsync(request, cancellationToken);

var categoryOptions = new QueryOptions<ProgramCategory>
Comment thread
Kitukl marked this conversation as resolved.
Outdated
{
Filter = category => request.createProgramDto.CategoriesId.Contains(category.Id),
AsNoTracking = false
};

var categories = await _repositoryWrapper
.ProgramCategoriesRepository.GetAllAsync(categoryOptions);

var entity = _mapper.Map<Program>(request.createProgramDto);

if (entity.ImageId != null)
{
var newImage = await _repositoryWrapper.ImageRepository.GetFirstOrDefaultAsync(new QueryOptions<Image>
{
Filter = image => image.Id == request.createProgramDto.ImageId,
AsNoTracking = false
});
if (newImage is not null)
{
try
Comment thread
Kitukl marked this conversation as resolved.
Outdated
{
newImage.Base64 = await _blobService.FindFileInStorageAsBase64Async(newImage.BlobName, newImage.MimeType);
}
catch(Exception)
{
return Result.Fail<ProgramDto>(ProgramConstants.FailedRetrievingProgramPhoto);
}
}

entity.Image = newImage;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

entity.Categories = categories.ToList();
entity.CreatedAt = DateTime.UtcNow;

await _repositoryWrapper.ProgramsRepository.CreateAsync(entity);

if (await _repositoryWrapper.SaveChangesAsync() > 0)
{
return Result.Ok(_mapper.Map<ProgramDto>(entity));
}

return Result.Fail<ProgramDto>(ProgramConstants.FailedToCreateProgram);
}
catch (ValidationException ex)
{
return Result.Fail<ProgramDto>(ex.Message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using MediatR;
using FluentResults;
namespace VictoryCenter.BLL.Commands.Programs.Delete;

public record DeleteProgramCommand(long Id) : IRequest<Result<long>>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using MediatR;
using FluentResults;
using Microsoft.EntityFrameworkCore;
using VictoryCenter.BLL.Constants;
using VictoryCenter.DAL.Entities;
using VictoryCenter.DAL.Repositories.Options;
using VictoryCenter.DAL.Repositories.Interfaces.Base;

namespace VictoryCenter.BLL.Commands.Programs.Delete;

public class DeleteProgramHandler : IRequestHandler<DeleteProgramCommand, Result<long>>
{
private readonly IRepositoryWrapper _repositoryWrapper;

public DeleteProgramHandler(IRepositoryWrapper repositoryWrapper)
{
_repositoryWrapper = repositoryWrapper;
}

public async Task<Result<long>> Handle(DeleteProgramCommand request, CancellationToken cancellationToken)
{
var entityToDelete = await _repositoryWrapper.ProgramsRepository.GetFirstOrDefaultAsync(new QueryOptions<Program>
{
Filter = program => program.Id == request.Id,
Include = program => program.Include(p => p.Categories)
});

if (entityToDelete is null)
{
return Result.Fail<long>(ErrorMessagesConstants
.NotFound(request.Id, typeof(Program)));
}

entityToDelete.Categories.Clear();
_repositoryWrapper.ProgramsRepository.Delete(entityToDelete);
Comment thread
Kitukl marked this conversation as resolved.

if (await _repositoryWrapper.SaveChangesAsync() > 0)
{
return Result.Ok(entityToDelete.Id);
}

return Result.Fail(ProgramConstants.FailedToDeleteProgram);
}
Comment thread
Kitukl marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using MediatR;
using FluentResults;
using VictoryCenter.BLL.DTOs.Programs;

namespace VictoryCenter.BLL.Commands.Programs.Update;

public record UpdateProgramCommand(UpdateProgramDto updateProgramDto) : IRequest<Result<ProgramDto>>;
Loading
Loading