From 594d6b908eb39fd4cf61b19cebb9cc263a8c0340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Mon, 15 Jun 2026 08:30:01 +0300 Subject: [PATCH 1/9] implament entities, dto, mediart, validators, tests for art templates gallery --- .../DTO/Media/Art/ArtCreateDto.cs | 11 + .../DTO/Media/Art/ArtUpdateDto.cs | 13 + .../DTO/Media/ArtSlides/ArtSlideItemDto.cs | 8 + .../ArtSlides/CreateStreetcodeArtSlideDto.cs | 14 + .../Media/ArtSlides/StreetcodeArtSlideDto.cs | 16 + .../ArtSlides/StreetcodeArtSlideItemDto.cs | 8 + .../DTO/Media/ArtSlides/UpdateArtSlideDto.cs | 16 + .../StreetcodeArtSlideTemplateDto.cs | 8 + .../Mapping/Media/Images/ArtProfile.cs | 4 + .../Mapping/Media/Images/ArtSlideProfile.cs | 22 + .../Media/Images/ArtSlideTeamplatesProfile.cs | 14 + .../Media/StreetcodeArtSlideProfile.cs | 19 + .../MediatR/Behaviors/ValidationBehavior.cs | 4 +- .../Media/Art/Create/CreateArtCommand.cs | 8 + .../Media/Art/Create/CreateArtHandler.cs | 29 + .../Media/Art/Delete/DeleteArtCommand.cs | 8 + .../Media/Art/Delete/DeleteArtHandler.cs | 34 + .../Media/Art/Update/UpdateArtCommand.cs | 8 + .../Media/Art/Update/UpdateArtHandler.cs | 39 + .../ArtSlide/Create/CreateArtSlideCommand.cs | 8 + .../ArtSlide/Create/CreateArtSlideHandler.cs | 72 + .../CreateAll/CreateAllArtSlidesCommand.cs | 9 + .../CreateAll/CreateAllArtSlidesHandler.cs | 81 + .../ArtSlide/Delete/DeleteArtSlideCommand.cs | 8 + .../ArtSlide/Delete/DeleteArtSlideHandler.cs | 44 + .../GetAllArtSlidesByStreetcodeIdHandler.cs | 36 + .../GetAllArtSlidesByStreetcodeIdQuery.cs | 9 + .../ArtSlide/Update/UpdateArtSlideCommand.cs | 8 + .../ArtSlide/Update/UpdateArtSlideHandler.cs | 59 + .../GetAll/GetAllArtSlideTemplatesHandler.cs | 40 + .../GetAll/GetAllArtSlideTemplatesQuery.cs | 8 + .../Resources/ErrorMessages.Designer.cs | 72 + .../Resources/ErrorMessages.resx | 24 + .../Media/Art/Create/ArtCreateDtoValidator.cs | 31 + .../Art/Create/CreateArtCommandValidator.cs | 20 + .../Art/Delete/DeleteArtCommandValidator.cs | 9 + .../Media/Art/Update/ArtUpdateDtoValidator.cs | 35 + .../Art/Update/UpdateArtCommandValidator.cs | 19 + .../ArtSlides/ArtSlideItemDtoValidator.cs | 22 + .../Create/CreateArtSlideCommandValidator.cs | 21 + .../CreateStreetcodeArtSlideDtoValidator.cs | 33 + .../CreateAllArtSlidesCommandValidator.cs | 24 + .../Delete/DeleteArtSlideCommandValidator.cs | 8 + ...llArtSlidesByStreetcodeIdQueryValidator.cs | 8 + .../Update/UpdateArtSlideCommandValidator.cs | 22 + .../Update/UpdateArtSlideDtoValidator.cs | 33 + .../StreetcodeArtSlideTemplateDtoValidator.cs | 28 + .../Entities/Media/Images/ArtSlideItem.cs | 13 + .../Media/Images/StreetcodeArtSlide.cs | 15 + .../Images/StreetcodeArtSlideTemplate.cs | 8 + .../Streetcode.DAL/Enums/ArtSlideTemplate.cs | 21 + .../ArtSlideItemConfiguration.cs | 22 + .../StreetcodeArtSlideConfiguration.cs | 29 + ...StreetcodeArtSlideTemplateConfiguration.cs | 27 + ...95326_AddArtSlideTemplateTable.Designer.cs | 2037 +++++++++++++++++ ...20260613095326_AddArtSlideTemplateTable.cs | 142 ++ .../StreetcodeDbContextModelSnapshot.cs | 180 ++ .../Persistence/StreetcodeDbContext.cs | 4 + .../Interfaces/Base/IRepositoryWrapper.cs | 4 + .../Media/Images/IArtSlideItemRepository.cs | 9 + .../Images/IStreetcodeArtSlideRepository.cs | 9 + .../IStreetcodeArtSlideTemplateRepository.cs | 9 + .../Realizations/Base/RepositoryWrapper.cs | 44 +- .../Media/Images/ArtRepository.cs | 3 +- .../Media/Images/ArtSlideItemRepository.cs | 14 + .../Images/StreetcodeArtSlideRepository.cs | 15 + .../StreetcodeArtSlideTemplateRepository.cs | 15 + .../Controllers/Media/ArtSlideController.cs | 61 + .../Media/ArtSlideTeamplatesController.cs | 18 + .../Controllers/Media/Images/ArtController.cs | 35 + .../Extensions/SeedingLocalExtension.cs | 85 +- Streetcode/Streetcode.WebApi/Program.cs | 2 +- .../Media/Art/Create/CreateArtHandlerTests.cs | 73 + .../Media/Art/Delete/DeleteArtHandlerTests.cs | 99 + .../Media/Art/Update/UpdateArtHandlerTests.cs | 129 ++ .../Create/CreateArtSlideHandlerTests.cs | 116 + .../CreateAllArtSlidesHandlerTests.cs | 76 + .../Delete/DeleteArtSlideHandlerTests.cs | 75 + ...tAllArtSlidesByStreetcodeIdHandlerTests.cs | 84 + .../Update/UpdateArtSlideHandlerTests.cs | 95 + .../GetAllArtSlideTemplatesHandlerTests.cs | 71 + .../Media/Art/ArtCreateDtoValidatorTests.cs | 41 + .../Media/Art/ArtUpdateDtoValidatorTests.cs | 31 + .../Art/CreateArtCommandValidatorTests.cs | 25 + .../Art/DeleteArtCommandValidatorTests.cs | 40 + .../Art/UpdateArtCommandValidatorTests.cs | 28 + .../ArtSlideItemDtoValidatorTests.cs | 40 + .../CreateArtSlideCommandValidatorTests.cs | 55 + ...eateStreetcodeArtSlideDtoValidatorTests.cs | 34 + ...CreateAllArtSlidesCommandValidatorTests.cs | 39 + .../DeleteArtSlideCommandValidatorTests.cs | 33 + ...SlidesByStreetcodeIdQueryValidatorTests.cs | 32 + .../UpdateArtSlideCommandValidatorTests.cs | 30 + .../Update/UpdateArtSlideDtoValidatorTests.cs | 34 + ...etcodeArtSlideTemplateDtoValidatorTests.cs | 44 + .../Media/Images/ArtControllerTests.cs | 107 + .../Media/Images/ArtSlideControllerTests.cs | 122 + .../ArtSlideTeamplatesControllerTests.cs | 64 + 98 files changed, 5462 insertions(+), 50 deletions(-) create mode 100644 Streetcode/Streetcode.BLL/DTO/Media/Art/ArtCreateDto.cs create mode 100644 Streetcode/Streetcode.BLL/DTO/Media/Art/ArtUpdateDto.cs create mode 100644 Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/ArtSlideItemDto.cs create mode 100644 Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/CreateStreetcodeArtSlideDto.cs create mode 100644 Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/StreetcodeArtSlideDto.cs create mode 100644 Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/StreetcodeArtSlideItemDto.cs create mode 100644 Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/UpdateArtSlideDto.cs create mode 100644 Streetcode/Streetcode.BLL/DTO/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDto.cs create mode 100644 Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtSlideProfile.cs create mode 100644 Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtSlideTeamplatesProfile.cs create mode 100644 Streetcode/Streetcode.BLL/Mapping/Media/StreetcodeArtSlideProfile.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtCommand.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtCommand.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtCommand.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideCommand.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesCommand.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideCommand.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQuery.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideCommand.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlideTeamplates/GetAll/GetAllArtSlideTemplatesHandler.cs create mode 100644 Streetcode/Streetcode.BLL/MediatR/Media/ArtSlideTeamplates/GetAll/GetAllArtSlideTemplatesQuery.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/Art/Create/ArtCreateDtoValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/Art/Create/CreateArtCommandValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/Art/Delete/DeleteArtCommandValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/Art/Update/ArtUpdateDtoValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/Art/Update/UpdateArtCommandValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/ArtSlideItemDtoValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Create/CreateArtSlideCommandValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Create/CreateStreetcodeArtSlideDtoValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/CreateAll/CreateAllArtSlidesCommandValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Delete/DeleteArtSlideCommandValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQueryValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideCommandValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideDtoValidator.cs create mode 100644 Streetcode/Streetcode.BLL/Validators/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDtoValidator.cs create mode 100644 Streetcode/Streetcode.DAL/Entities/Media/Images/ArtSlideItem.cs create mode 100644 Streetcode/Streetcode.DAL/Entities/Media/Images/StreetcodeArtSlide.cs create mode 100644 Streetcode/Streetcode.DAL/Entities/Media/Images/StreetcodeArtSlideTemplate.cs create mode 100644 Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs create mode 100644 Streetcode/Streetcode.DAL/Persistence/Configurations/ArtSlideItemConfiguration.cs create mode 100644 Streetcode/Streetcode.DAL/Persistence/Configurations/StreetcodeArtSlideConfiguration.cs create mode 100644 Streetcode/Streetcode.DAL/Persistence/Configurations/StreetcodeArtSlideTemplateConfiguration.cs create mode 100644 Streetcode/Streetcode.DAL/Persistence/Migrations/20260613095326_AddArtSlideTemplateTable.Designer.cs create mode 100644 Streetcode/Streetcode.DAL/Persistence/Migrations/20260613095326_AddArtSlideTemplateTable.cs create mode 100644 Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IArtSlideItemRepository.cs create mode 100644 Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IStreetcodeArtSlideRepository.cs create mode 100644 Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IStreetcodeArtSlideTemplateRepository.cs create mode 100644 Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/ArtSlideItemRepository.cs create mode 100644 Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/StreetcodeArtSlideRepository.cs create mode 100644 Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/StreetcodeArtSlideTemplateRepository.cs create mode 100644 Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideController.cs create mode 100644 Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideTeamplatesController.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Create/CreateArtHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/ArtCreateDtoValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/ArtUpdateDtoValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/CreateArtCommandValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/DeleteArtCommandValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/UpdateArtCommandValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/ArtSlideItemDtoValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Create/CreateArtSlideCommandValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Create/CreateStreetcodeArtSlideDtoValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/CreateAll/CreateAllArtSlidesCommandValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Delete/DeleteArtSlideCommandValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQueryValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideCommandValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideDtoValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDtoValidatorTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtSlideControllerTests.cs create mode 100644 Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtSlideTeamplatesControllerTests.cs diff --git a/Streetcode/Streetcode.BLL/DTO/Media/Art/ArtCreateDto.cs b/Streetcode/Streetcode.BLL/DTO/Media/Art/ArtCreateDto.cs new file mode 100644 index 00000000..32252217 --- /dev/null +++ b/Streetcode/Streetcode.BLL/DTO/Media/Art/ArtCreateDto.cs @@ -0,0 +1,11 @@ +using Streetcode.BLL.DTO.Media.Images; + +namespace Streetcode.BLL.DTO.Media.Art +{ + public class ArtCreateDto + { + public string? Description { get; set; } + public string? Title { get; set; } + public int ImageId { get; set; } + } +} diff --git a/Streetcode/Streetcode.BLL/DTO/Media/Art/ArtUpdateDto.cs b/Streetcode/Streetcode.BLL/DTO/Media/Art/ArtUpdateDto.cs new file mode 100644 index 00000000..eadd4283 --- /dev/null +++ b/Streetcode/Streetcode.BLL/DTO/Media/Art/ArtUpdateDto.cs @@ -0,0 +1,13 @@ +using Streetcode.BLL.DTO.Media.Images; + +namespace Streetcode.BLL.DTO.Media.Art +{ + public class ArtUpdateDto + { + public int Id { get; set; } + public string? Description { get; set; } + public string? Title { get; set; } + public int ImageId { get; set; } + public ImageDTO? Image { get; set; } + } +} diff --git a/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/ArtSlideItemDto.cs b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/ArtSlideItemDto.cs new file mode 100644 index 00000000..cb69cb1b --- /dev/null +++ b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/ArtSlideItemDto.cs @@ -0,0 +1,8 @@ +namespace Streetcode.BLL.DTO.Media.ArtSlides +{ + public class ArtSlideItemDto + { + public int ArtId { get; set; } + public int Index { get; set; } + } +} diff --git a/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/CreateStreetcodeArtSlideDto.cs b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/CreateStreetcodeArtSlideDto.cs new file mode 100644 index 00000000..bece6cdd --- /dev/null +++ b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/CreateStreetcodeArtSlideDto.cs @@ -0,0 +1,14 @@ +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.DAL.Entities.Media.Images; + +namespace Streetcode.BLL.DTO.Media.ArtSlides +{ + public class CreateStreetcodeArtSlideDto + { + public int Index { get; set; } + public int TemplateId { get; set; } + public int StreetcodeId { get; set; } + + public List ArtSlideItems { get; set; } = new(); + } +} diff --git a/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/StreetcodeArtSlideDto.cs b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/StreetcodeArtSlideDto.cs new file mode 100644 index 00000000..268736e6 --- /dev/null +++ b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/StreetcodeArtSlideDto.cs @@ -0,0 +1,16 @@ +using Streetcode.DAL.Entities.Media.Images; + +namespace Streetcode.BLL.DTO.Media.ArtSlides +{ + public class StreetcodeArtSlideDto + { + public int Id { get; set; } + public int Index { get; set; } + + public int TemplateId { get; set; } + public StreetcodeArtSlideTemplate Template { get; set; } = null!; + + public List ArtSlideItems { get; set; } = new(); + public int StreetcodeId { get; set; } + } +} diff --git a/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/StreetcodeArtSlideItemDto.cs b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/StreetcodeArtSlideItemDto.cs new file mode 100644 index 00000000..e374a077 --- /dev/null +++ b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/StreetcodeArtSlideItemDto.cs @@ -0,0 +1,8 @@ +namespace Streetcode.BLL.DTO.Media.ArtSlides +{ + public class StreetcodeArtSlideItemDto + { + public int ArtId { get; set; } + public int Index { get; set; } + } +} diff --git a/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/UpdateArtSlideDto.cs b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/UpdateArtSlideDto.cs new file mode 100644 index 00000000..c9ff2d85 --- /dev/null +++ b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlides/UpdateArtSlideDto.cs @@ -0,0 +1,16 @@ +using Streetcode.DAL.Entities.Media.Images; + +namespace Streetcode.BLL.DTO.Media.ArtSlides +{ + public class UpdateArtSlideDto + { + public int Id { get; set; } + public int Index { get; set; } + + public int TemplateId { get; set; } + public StreetcodeArtSlideTemplate Template { get; set; } = null!; + + public List ArtSlideItems { get; set; } = new(); + public int StreetcodeId { get; set; } + } +} diff --git a/Streetcode/Streetcode.BLL/DTO/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDto.cs b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDto.cs new file mode 100644 index 00000000..c793dc8f --- /dev/null +++ b/Streetcode/Streetcode.BLL/DTO/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDto.cs @@ -0,0 +1,8 @@ +namespace Streetcode.BLL.DTO.Media.ArtSlidesTemplates +{ + public class StreetcodeArtSlideTemplateDto + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + } +} diff --git a/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtProfile.cs b/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtProfile.cs index f82660f7..a02fb6bc 100644 --- a/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtProfile.cs +++ b/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtProfile.cs @@ -10,5 +10,9 @@ public class ArtProfile : Profile public ArtProfile() { CreateMap().ReverseMap(); + CreateMap() + .ForMember(dest => dest.ImageId, opt => opt.MapFrom(src => src.ImageId)) + .ForMember(dest => dest.Image, opt => opt.Ignore()); + CreateMap(); } } diff --git a/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtSlideProfile.cs b/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtSlideProfile.cs new file mode 100644 index 00000000..dc7ca593 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtSlideProfile.cs @@ -0,0 +1,22 @@ +using AutoMapper; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.DAL.Entities.Media.Images; + +namespace Streetcode.BLL.Mapping.Media.Images +{ + public class ArtSlideProfile : Profile + { + public ArtSlideProfile() + { + CreateMap() + .ForMember(dest => dest.ArtSlideItems, opt => opt.Ignore()) + .ReverseMap(); + + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtSlideTeamplatesProfile.cs b/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtSlideTeamplatesProfile.cs new file mode 100644 index 00000000..87297172 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Mapping/Media/Images/ArtSlideTeamplatesProfile.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using Streetcode.BLL.DTO.Media.ArtSlidesTemplates; +using Streetcode.DAL.Entities.Media.Images; + +namespace Streetcode.BLL.Mapping.Media.Images +{ + internal class ArtSlideTeamplatesProfile : Profile + { + public ArtSlideTeamplatesProfile() + { + CreateMap().ReverseMap(); + } + } +} diff --git a/Streetcode/Streetcode.BLL/Mapping/Media/StreetcodeArtSlideProfile.cs b/Streetcode/Streetcode.BLL/Mapping/Media/StreetcodeArtSlideProfile.cs new file mode 100644 index 00000000..9b06874c --- /dev/null +++ b/Streetcode/Streetcode.BLL/Mapping/Media/StreetcodeArtSlideProfile.cs @@ -0,0 +1,19 @@ +using AutoMapper; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.DAL.Entities.Media.Images; + +namespace Streetcode.BLL.Mapping.Media +{ + public class StreetcodeArtSlideProfile : Profile + { + public StreetcodeArtSlideProfile() + { + CreateMap() + .ForMember(dto => dto.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dto => dto.Template, opt => opt.MapFrom(src => src.TemplateId)) + .ReverseMap() + .ForMember(src => src.Id, opt => opt.MapFrom(dto => dto.Id)) + .ForMember(src => src.TemplateId, opt => opt.MapFrom(dto => dto.Template)); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Behaviors/ValidationBehavior.cs b/Streetcode/Streetcode.BLL/MediatR/Behaviors/ValidationBehavior.cs index d5c146c0..9134fd5a 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Behaviors/ValidationBehavior.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Behaviors/ValidationBehavior.cs @@ -23,7 +23,9 @@ public async Task Handle(TRequest request, RequestHandlerDelegate(request); - var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken))); + var validationResults = await Task.WhenAll( + _validators.Select(v => + v.ValidateAsync(context, CancellationToken.None))); var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList(); if (failures.Count != 0) diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtCommand.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtCommand.cs new file mode 100644 index 00000000..5b28f253 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtCommand.cs @@ -0,0 +1,8 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.Art; + +namespace Streetcode.BLL.MediatR.Media.Art.Create +{ + public record CreateArtCommand(ArtCreateDto ArtDto) : IRequest>; +} diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtHandler.cs new file mode 100644 index 00000000..d28cee0d --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtHandler.cs @@ -0,0 +1,29 @@ +using AutoMapper; +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.BLL.MediatR.Media.Art.Create +{ + public class CreateArtHandler : IRequestHandler> + { + private readonly IMapper _mapper; + private readonly IRepositoryWrapper _repositoryWrapper; + + public CreateArtHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) + { + _mapper = mapper; + _repositoryWrapper = repositoryWrapper; + } + + public async Task> Handle(CreateArtCommand request, CancellationToken ct) + { + var art = _mapper.Map(request.ArtDto); + await _repositoryWrapper.ArtRepository.CreateAsync(art); + await _repositoryWrapper.SaveChangesAsync(); + + return Result.Ok(_mapper.Map(art)); + } + } +} diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtCommand.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtCommand.cs new file mode 100644 index 00000000..c0cc24e5 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtCommand.cs @@ -0,0 +1,8 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.MediatR.Interface; + +namespace Streetcode.BLL.MediatR.Media.Art.Delete +{ + public record DeleteArtCommand(int Id) : IRequest>, IHasId; +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs new file mode 100644 index 00000000..f1da44e6 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs @@ -0,0 +1,34 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.BLL.MediatR.Media.Art.Delete +{ + public class DeleteArtHandler : IRequestHandler> + { + private readonly IRepositoryWrapper _repositoryWrapper; + + public DeleteArtHandler(IRepositoryWrapper repositoryWrapper) + { + _repositoryWrapper = repositoryWrapper; + } + + public async Task> Handle(DeleteArtCommand request, CancellationToken ct) + { + var art = await _repositoryWrapper.ArtRepository + .GetFirstOrDefaultAsync(a => a.Id == request.Id); + + if (art == null) + { + return Result.Fail(string.Format(ErrorMessages.EntityNotFound, request.Id)); + } + + _repositoryWrapper.ArtRepository.Delete(art); + + await _repositoryWrapper.SaveChangesAsync(ct); + + return Result.Ok(Unit.Value); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtCommand.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtCommand.cs new file mode 100644 index 00000000..cbcc7663 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtCommand.cs @@ -0,0 +1,8 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.Art; + +namespace Streetcode.BLL.MediatR.Media.Art.Update +{ + public record UpdateArtCommand(ArtUpdateDto ArtDto) : IRequest>; +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs new file mode 100644 index 00000000..11c26c12 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs @@ -0,0 +1,39 @@ +using AutoMapper; +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.BLL.MediatR.Media.Art.Update +{ + public class UpdateArtHandler : IRequestHandler> + { + private readonly IMapper _mapper; + private readonly IRepositoryWrapper _repositoryWrapper; + + public UpdateArtHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) + { + _mapper = mapper; + _repositoryWrapper = repositoryWrapper; + } + + public async Task> Handle(UpdateArtCommand request, CancellationToken ct) + { + var art = await _repositoryWrapper.ArtRepository + .GetFirstOrDefaultAsync(a => a.Id == request.ArtDto.Id); + + if (art == null) + { + return Result.Fail(string.Format(ErrorMessages.EntityNotFound, request.ArtDto.Id)); + } + + _mapper.Map(request.ArtDto, art); + + _repositoryWrapper.ArtRepository.Update(art); + await _repositoryWrapper.SaveChangesAsync(ct); + + return Result.Ok(_mapper.Map(art)); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideCommand.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideCommand.cs new file mode 100644 index 00000000..3b0c0d4e --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideCommand.cs @@ -0,0 +1,8 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.ArtSlides; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.Create +{ + public record CreateArtSlideCommand(CreateStreetcodeArtSlideDto Dto) : IRequest>; +} diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs new file mode 100644 index 00000000..18a8d285 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs @@ -0,0 +1,72 @@ +using AutoMapper; +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.MediatR.Media.ArtSlide.Create; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.Create +{ + public class CreateArtSlideHandler : IRequestHandler> + { + private readonly IMapper _mapper; + private readonly IRepositoryWrapper _repositoryWrapper; + + public CreateArtSlideHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) + { + _mapper = mapper; + _repositoryWrapper = repositoryWrapper; + } + + public async Task> Handle(CreateArtSlideCommand request, CancellationToken ct) + { + var streetcode = await _repositoryWrapper.StreetcodeRepository + .GetFirstOrDefaultAsync(s => s.Id == request.Dto.StreetcodeId); + + if (streetcode == null) + { + return Result.Fail(string.Format(ErrorMessages.StreetcodeNotFound, request.Dto.StreetcodeId)); + } + + var artIdsFromDto = request.Dto.ArtSlideItems.Select(a => a.ArtId).Distinct().ToList(); + + var existingArts = await _repositoryWrapper.ArtRepository + .GetAllAsync(a => artIdsFromDto.Contains(a.Id)); + + if (existingArts.Count() != artIdsFromDto.Count) + { + return Result.Fail(ErrorMessages.ArtsNotFound); + } + + using (var transaction = _repositoryWrapper.BeginTransaction()) + { + var newSlide = _mapper.Map(request.Dto); + _repositoryWrapper.StreetcodeArtSlideRepository.Create(newSlide); + await _repositoryWrapper.SaveChangesAsync(ct); + + foreach (var artItem in request.Dto.ArtSlideItems) + { + var artExists = await _repositoryWrapper.ArtRepository.GetFirstOrDefaultAsync(a => a.Id == artItem.ArtId); + if (artExists == null) + { + return Result.Fail($"Art with id {artItem.ArtId} does not exist."); + } + _repositoryWrapper.ArtSlideItemRepository.Create(new ArtSlideItem + { + SlideId = newSlide.Id, + ArtId = artItem.ArtId, + Index = artItem.Index + }); + } + + await _repositoryWrapper.SaveChangesAsync(ct); + + transaction.Complete(); + + return Result.Ok(_mapper.Map(newSlide)); + } + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesCommand.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesCommand.cs new file mode 100644 index 00000000..b1cef99e --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesCommand.cs @@ -0,0 +1,9 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.ArtSlides; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.CreateAll +{ + public record CreateAllArtSlidesCommand(List ArtSlides) + : IRequest>>; +} diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs new file mode 100644 index 00000000..bb0880e0 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs @@ -0,0 +1,81 @@ +using AutoMapper; +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.MediatR.Media.ArtSlide.CreateAll; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.Create +{ + public class CreateAllArtSlidesHandler : IRequestHandler>> + { + private readonly IMapper _mapper; + private readonly IRepositoryWrapper _repositoryWrapper; + + public CreateAllArtSlidesHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) + { + _mapper = mapper; + _repositoryWrapper = repositoryWrapper; + } + + public async Task>> Handle( + CreateAllArtSlidesCommand request, + CancellationToken ct) + { + if (request.ArtSlides == null || !request.ArtSlides.Any()) + { + return Result.Fail("ArtSlides list is empty"); + } + + var streetcodeId = request.ArtSlides.First().StreetcodeId; + + var streetcode = await _repositoryWrapper.StreetcodeRepository + .GetFirstOrDefaultAsync(s => s.Id == streetcodeId); + + if (streetcode == null) + { + return Result.Fail( + string.Format(ErrorMessages.StreetcodeNotFound, streetcodeId)); + } + + var resultList = new List(); + + using (var transaction = _repositoryWrapper.BeginTransaction()) + { + foreach (var dto in request.ArtSlides) + { + if (dto.StreetcodeId != streetcodeId) + { + return Result.Fail("All slides must belong to the same StreetcodeId"); + } + + var newSlide = _mapper.Map(dto); + + _repositoryWrapper.StreetcodeArtSlideRepository.Create(newSlide); + await _repositoryWrapper.SaveChangesAsync(ct); + + foreach (var artItem in dto.ArtSlideItems) + { + _repositoryWrapper.ArtSlideItemRepository.Create( + new ArtSlideItem + { + SlideId = newSlide.Id, + ArtId = artItem.ArtId, + Index = artItem.Index + }); + } + + await _repositoryWrapper.SaveChangesAsync(ct); + + resultList.Add(_mapper.Map(newSlide)); + } + + transaction.Complete(); + } + + return Result.Ok(resultList.AsEnumerable()); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideCommand.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideCommand.cs new file mode 100644 index 00000000..be8e8615 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideCommand.cs @@ -0,0 +1,8 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.MediatR.Interface; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.Delete +{ + public record DeleteArtSlideCommand(int Id) : IRequest>, IHasId; +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs new file mode 100644 index 00000000..b98874cf --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs @@ -0,0 +1,44 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.Delete +{ + public class DeleteArtSlideHandler : IRequestHandler> + { + private readonly IRepositoryWrapper _repositoryWrapper; + + public DeleteArtSlideHandler(IRepositoryWrapper repositoryWrapper) + { + _repositoryWrapper = repositoryWrapper; + } + + public async Task> Handle(DeleteArtSlideCommand request, CancellationToken ct) + { + var slide = await _repositoryWrapper.StreetcodeArtSlideRepository + .GetFirstOrDefaultAsync(s => s.Id == request.Id); + + if (slide == null) + { + return Result.Fail(string.Format(ErrorMessages.SlideNotFound, request.Id)); + } + + using (var transaction = _repositoryWrapper.BeginTransaction()) + { + var items = await _repositoryWrapper.ArtSlideItemRepository + .GetAllAsync(i => i.SlideId == request.Id); + + _repositoryWrapper.ArtSlideItemRepository.DeleteRange(items); + + _repositoryWrapper.StreetcodeArtSlideRepository.Delete(slide); + + await _repositoryWrapper.SaveChangesAsync(ct); + + transaction.Complete(); + } + + return Result.Ok(Unit.Value); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandler.cs new file mode 100644 index 00000000..e54a9c3c --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandler.cs @@ -0,0 +1,36 @@ +using AutoMapper; +using FluentResults; +using MediatR; +using Microsoft.EntityFrameworkCore; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.GetAllByStreetcodeId +{ + public class GetAllArtSlidesByStreetcodeIdHandler : IRequestHandler>> + { + private readonly IMapper _mapper; + private readonly IRepositoryWrapper _repositoryWrapper; + + public GetAllArtSlidesByStreetcodeIdHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) + { + _mapper = mapper; + _repositoryWrapper = repositoryWrapper; + } + + public async Task>> Handle(GetAllArtSlidesByStreetcodeIdQuery request, CancellationToken ct) + { + var slides = await _repositoryWrapper.StreetcodeArtSlideRepository.GetAllAsync( + predicate: s => s.StreetcodeId == request.StreetcodeId, + include: q => q.Include(s => s.ArtSlideItems) + ); + + if (slides == null) + { + return Result.Fail("Slides not found"); + } + + return Result.Ok(_mapper.Map>(slides)); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQuery.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQuery.cs new file mode 100644 index 00000000..130beacc --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQuery.cs @@ -0,0 +1,9 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.MediatR.Interface; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.GetAllByStreetcodeId +{ + public record GetAllArtSlidesByStreetcodeIdQuery(int StreetcodeId) : IRequest>>, IHasStreetcodeId; +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideCommand.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideCommand.cs new file mode 100644 index 00000000..bf392cde --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideCommand.cs @@ -0,0 +1,8 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.ArtSlides; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.Update +{ + public record UpdateArtSlideCommand(UpdateArtSlideDto Dto) : IRequest>; +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs new file mode 100644 index 00000000..90751cd3 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs @@ -0,0 +1,59 @@ +using AutoMapper; +using FluentResults; +using MediatR; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.BLL.MediatR.Media.ArtSlide.Update +{ + public class UpdateArtSlideHandler : IRequestHandler> + { + private readonly IMapper _mapper; + private readonly IRepositoryWrapper _repositoryWrapper; + + public UpdateArtSlideHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) + { + _mapper = mapper; + _repositoryWrapper = repositoryWrapper; + } + + public async Task> Handle(UpdateArtSlideCommand request, CancellationToken ct) + { + var slide = await _repositoryWrapper.StreetcodeArtSlideRepository + .GetFirstOrDefaultAsync(s => s.Id == request.Dto.Id); + + if (slide == null) + { + return Result.Fail(string.Format(ErrorMessages.SlideNotFound, request.Dto.Id)); + } + + using (var transaction = _repositoryWrapper.BeginTransaction()) + { + slide.Index = request.Dto.Index; + slide.TemplateId = request.Dto.TemplateId; + _repositoryWrapper.StreetcodeArtSlideRepository.Update(slide); + + var oldItems = await _repositoryWrapper.ArtSlideItemRepository + .GetAllAsync(i => i.SlideId == slide.Id); + + _repositoryWrapper.ArtSlideItemRepository.DeleteRange(oldItems); + + foreach (var item in request.Dto.ArtSlideItems) + { + _repositoryWrapper.ArtSlideItemRepository.Create(new ArtSlideItem + { + SlideId = slide.Id, + ArtId = item.ArtId, + Index = item.Index + }); + } + + await _repositoryWrapper.SaveChangesAsync(ct); + transaction.Complete(); + } + + return Result.Ok(Unit.Value); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlideTeamplates/GetAll/GetAllArtSlideTemplatesHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlideTeamplates/GetAll/GetAllArtSlideTemplatesHandler.cs new file mode 100644 index 00000000..99230878 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlideTeamplates/GetAll/GetAllArtSlideTemplatesHandler.cs @@ -0,0 +1,40 @@ +using AutoMapper; +using FluentResults; +using MediatR; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Streetcode.BLL.Interfaces.Logging; +using Streetcode.BLL.DTO.Media.ArtSlidesTemplates; + +namespace Streetcode.BLL.MediatR.Media.ArtSlideTeamplates.GetAll +{ + public class GetAllArtSlideTemplatesHandler : IRequestHandler>> + { + private readonly IMapper _mapper; + private readonly IRepositoryWrapper _repositoryWrapper; + private readonly ILoggerService _logger; + + public GetAllArtSlideTemplatesHandler(IRepositoryWrapper repositoryWrapper, IMapper mapper, ILoggerService logger) + { + _repositoryWrapper = repositoryWrapper; + _mapper = mapper; + _logger = logger; + } + + public async Task>> Handle(GetAllArtSlideTemplatesQuery request, CancellationToken cancellationToken) + { + var templates = await _repositoryWrapper.StreetcodeArtSlideTemplateRepository.GetAllAsync(); + + if (templates is null || !templates.Any()) + { + string errorMsg = ErrorMessages.CannotFindAnyArtSlideTemplates; + _logger.LogError(request, errorMsg); + return Result.Fail(new Error(errorMsg)); + } + + var templateDtos = _mapper.Map>(templates); + + return Result.Ok(templateDtos); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlideTeamplates/GetAll/GetAllArtSlideTemplatesQuery.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlideTeamplates/GetAll/GetAllArtSlideTemplatesQuery.cs new file mode 100644 index 00000000..6529df9d --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlideTeamplates/GetAll/GetAllArtSlideTemplatesQuery.cs @@ -0,0 +1,8 @@ +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.ArtSlidesTemplates; + +namespace Streetcode.BLL.MediatR.Media.ArtSlideTeamplates.GetAll +{ + public record GetAllArtSlideTemplatesQuery() : IRequest>>; +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.Designer.cs b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.Designer.cs index c467faff..6c2ace5b 100644 --- a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.Designer.cs +++ b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.Designer.cs @@ -87,6 +87,24 @@ public static string AmountMustBeGreaterThanZero { } } + /// + /// Looks up a localized string similar to Art slide items are required.. + /// + public static string ArtSlideItemsIsRequired { + get { + return ResourceManager.GetString("ArtSlideItemsIsRequired", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to One or more specified arts were not found.. + /// + public static string ArtsNotFound { + get { + return ResourceManager.GetString("ArtsNotFound", resourceCulture); + } + } + /// /// Looks up a localized string similar to Audio is required.. /// @@ -168,6 +186,15 @@ public static string CannotFindAnyArts { } } + /// + /// Looks up a localized string similar to Cannot find any art slide templates. + /// + public static string CannotFindAnyArtSlideTemplates { + get { + return ResourceManager.GetString("CannotFindAnyArtSlideTemplates", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot find any audios. /// @@ -627,6 +654,15 @@ public static string EndDateCannotBeEarlierThanStartDate { } } + /// + /// Looks up a localized string similar to Entity is not found. + /// + public static string EntityNotFound { + get { + return ResourceManager.GetString("EntityNotFound", resourceCulture); + } + } + /// /// Looks up a localized string similar to Extension is required.. /// @@ -978,6 +1014,15 @@ public static string InvalidEnumValue { } } + /// + /// Looks up a localized string similar to Id must be greater than 0. + /// + public static string InvalidId { + get { + return ResourceManager.GetString("InvalidId", resourceCulture); + } + } + /// /// Looks up a localized string similar to The provided logo type is invalid.. /// @@ -1068,6 +1113,15 @@ public static string LongitudeMustBeBetweenMinus180And180 { } } + /// + /// Looks up a localized string similar to {0} must not exceed {1} characters.. + /// + public static string MaxLengthError { + get { + return ResourceManager.GetString("MaxLengthError", resourceCulture); + } + } + /// /// Looks up a localized string similar to MIME type is required.. /// @@ -1320,6 +1374,15 @@ public static string RelatedWordAlreadyExists { } } + /// + /// Looks up a localized string similar to Request is required.. + /// + public static string RequestIsRequired { + get { + return ResourceManager.GetString("RequestIsRequired", resourceCulture); + } + } + /// /// Looks up a localized string similar to Search query is required.. /// @@ -1338,6 +1401,15 @@ public static string SearchQueryMustNotExceedCharacters { } } + /// + /// Looks up a localized string similar to Slide with ID {0} was not found.. + /// + public static string SlideNotFound { + get { + return ResourceManager.GetString("SlideNotFound", resourceCulture); + } + } + /// /// Looks up a localized string similar to Source category already exists for this streetcode. /// diff --git a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx index 413b5792..5292f9c4 100644 --- a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx +++ b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx @@ -693,4 +693,28 @@ Found result matching null + + Cannot find any art slide templates + + + One or more specified arts were not found. + + + Slide with ID {0} was not found. + + + Entity is not found + + + Id must be greater than 0 + + + Art slide items are required. + + + Request is required. + + + {0} must not exceed {1} characters. + \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/Art/Create/ArtCreateDtoValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/Art/Create/ArtCreateDtoValidator.cs new file mode 100644 index 00000000..4b71c9ec --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/Art/Create/ArtCreateDtoValidator.cs @@ -0,0 +1,31 @@ +using FluentValidation; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.Resources; + +namespace Streetcode.BLL.Validators.Media.Art.Create +{ + public class ArtCreateDtoValidator : AbstractValidator + { + public const int MaxTitleLength = 150; + public const int MaxDescriptionLength = 1000; + + public ArtCreateDtoValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(dto => dto.ImageId) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(dto => dto.Title) + .NotEmpty() + .WithMessage(ErrorMessages.TitleIsRequired) + .MaximumLength(MaxTitleLength) + .WithMessage(string.Format(ErrorMessages.MaxLengthError, "Title", MaxTitleLength)); + + RuleFor(dto => dto.Description) + .MaximumLength(MaxDescriptionLength) + .WithMessage(string.Format(ErrorMessages.MaxLengthError, "Description", MaxDescriptionLength)); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/Art/Create/CreateArtCommandValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/Art/Create/CreateArtCommandValidator.cs new file mode 100644 index 00000000..5d04b8eb --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/Art/Create/CreateArtCommandValidator.cs @@ -0,0 +1,20 @@ +using FluentValidation; +using Streetcode.BLL.MediatR.Media.Art.Create; +using Streetcode.BLL.Resources; +using Streetcode.BLL.Validators.Media.Art.Create; + +namespace Streetcode.BLL.Validators.Media.Art +{ + public class CreateArtCommandValidator : AbstractValidator + { + public CreateArtCommandValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(command => command.ArtDto) + .NotNull() + .WithMessage(ErrorMessages.RequestIsRequired) + .SetValidator(new ArtCreateDtoValidator()); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/Art/Delete/DeleteArtCommandValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/Art/Delete/DeleteArtCommandValidator.cs new file mode 100644 index 00000000..cf08cc2f --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/Art/Delete/DeleteArtCommandValidator.cs @@ -0,0 +1,9 @@ +using Streetcode.BLL.MediatR.Media.Art.Delete; +using Streetcode.BLL.MediatR.Media.Image.Delete; + +namespace Streetcode.BLL.Validators.Media.Art.Delete +{ + internal class DeleteArtCommandValidator : PositiveIdValidator + { + } +} diff --git a/Streetcode/Streetcode.BLL/Validators/Media/Art/Update/ArtUpdateDtoValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/Art/Update/ArtUpdateDtoValidator.cs new file mode 100644 index 00000000..b5d41955 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/Art/Update/ArtUpdateDtoValidator.cs @@ -0,0 +1,35 @@ +using FluentValidation; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.Resources; + +namespace Streetcode.BLL.Validators.Media.Art +{ + public class ArtUpdateDtoValidator : AbstractValidator + { + public const int MaxTitleLength = 150; + public const int MaxDescriptionLength = 1000; + + public ArtUpdateDtoValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(dto => dto.Id) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(dto => dto.ImageId) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(dto => dto.Title) + .NotEmpty() + .WithMessage(ErrorMessages.TitleIsRequired) + .MaximumLength(MaxTitleLength) + .WithMessage(string.Format(ErrorMessages.MaxLengthError, "Title", MaxTitleLength)); + + RuleFor(dto => dto.Description) + .MaximumLength(MaxDescriptionLength) + .WithMessage(string.Format(ErrorMessages.MaxLengthError, "Description", MaxDescriptionLength)); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/Art/Update/UpdateArtCommandValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/Art/Update/UpdateArtCommandValidator.cs new file mode 100644 index 00000000..10386480 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/Art/Update/UpdateArtCommandValidator.cs @@ -0,0 +1,19 @@ +using FluentValidation; +using Streetcode.BLL.MediatR.Media.Art.Update; +using Streetcode.BLL.Resources; + +namespace Streetcode.BLL.Validators.Media.Art +{ + public class UpdateArtCommandValidator : AbstractValidator + { + public UpdateArtCommandValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(command => command.ArtDto) + .NotNull() + .WithMessage(ErrorMessages.RequestIsRequired) + .SetValidator(new ArtUpdateDtoValidator()); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/ArtSlideItemDtoValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/ArtSlideItemDtoValidator.cs new file mode 100644 index 00000000..868ace8c --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/ArtSlideItemDtoValidator.cs @@ -0,0 +1,22 @@ +using FluentValidation; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Resources; + +namespace Streetcode.BLL.Validators.Media.ArtSlides +{ + public class ArtSlideItemDtoValidator : AbstractValidator + { + public ArtSlideItemDtoValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.ArtId) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(x => x.Index) + .GreaterThanOrEqualTo(0) + .WithMessage(ErrorMessages.IndexMustBeGreaterOrEqualToZero); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Create/CreateArtSlideCommandValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Create/CreateArtSlideCommandValidator.cs new file mode 100644 index 00000000..e70d8100 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Create/CreateArtSlideCommandValidator.cs @@ -0,0 +1,21 @@ +using FluentValidation; +using Streetcode.BLL.MediatR.Media.ArtSlide.Create; +using Streetcode.BLL.Resources; + +namespace Streetcode.BLL.Validators.Media.ArtSlides.Create +{ + public class CreateArtSlideCommandValidator : AbstractValidator + { + public CreateArtSlideCommandValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(command => command.Dto) + .NotNull() + .WithMessage(ErrorMessages.RequestIsRequired); + + RuleFor(command => command.Dto) + .SetValidator(new CreateStreetcodeArtSlideDtoValidator()); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Create/CreateStreetcodeArtSlideDtoValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Create/CreateStreetcodeArtSlideDtoValidator.cs new file mode 100644 index 00000000..3919d448 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Create/CreateStreetcodeArtSlideDtoValidator.cs @@ -0,0 +1,33 @@ +using FluentValidation; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Resources; + +namespace Streetcode.BLL.Validators.Media.ArtSlides.Create +{ + public class CreateStreetcodeArtSlideDtoValidator : AbstractValidator + { + public CreateStreetcodeArtSlideDtoValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.Index) + .GreaterThanOrEqualTo(0) + .WithMessage(ErrorMessages.IndexMustBeGreaterOrEqualToZero); + + RuleFor(x => x.TemplateId) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(x => x.StreetcodeId) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(x => x.ArtSlideItems) + .NotNull() + .WithMessage(ErrorMessages.ArtSlideItemsIsRequired); + + RuleForEach(x => x.ArtSlideItems) + .SetValidator(new ArtSlideItemDtoValidator()); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/CreateAll/CreateAllArtSlidesCommandValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/CreateAll/CreateAllArtSlidesCommandValidator.cs new file mode 100644 index 00000000..f01f3d63 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/CreateAll/CreateAllArtSlidesCommandValidator.cs @@ -0,0 +1,24 @@ +using FluentValidation; +using Streetcode.BLL.MediatR.Media.ArtSlide.CreateAll; +using Streetcode.BLL.Resources; +using Streetcode.BLL.Validators.Media.ArtSlides.Create; + +namespace Streetcode.BLL.Validators.Media.ArtSlides +{ + public class CreateAllArtSlidesCommandValidator : AbstractValidator + { + public CreateAllArtSlidesCommandValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(command => command.ArtSlides) + .NotNull() + .WithMessage(ErrorMessages.ArtSlideItemsIsRequired) + .NotEmpty() + .WithMessage(ErrorMessages.ArtSlideItemsIsRequired); + + RuleForEach(command => command.ArtSlides) + .SetValidator(new CreateStreetcodeArtSlideDtoValidator()); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Delete/DeleteArtSlideCommandValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Delete/DeleteArtSlideCommandValidator.cs new file mode 100644 index 00000000..a17b90b9 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Delete/DeleteArtSlideCommandValidator.cs @@ -0,0 +1,8 @@ +using Streetcode.BLL.MediatR.Media.ArtSlide.Delete; + +namespace Streetcode.BLL.Validators.Media.ArtSlides.Delete +{ + internal class DeleteArtSlideCommandValidator : PositiveIdValidator + { + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQueryValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQueryValidator.cs new file mode 100644 index 00000000..d2976b36 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQueryValidator.cs @@ -0,0 +1,8 @@ +using Streetcode.BLL.MediatR.Media.ArtSlide.GetAllByStreetcodeId; + +namespace Streetcode.BLL.Validators.Media.ArtSlides.GetByStreetcodeId +{ + internal class GetAllArtSlidesByStreetcodeIdQueryValidator : PositiveStreetcodeIdValidator + { + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideCommandValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideCommandValidator.cs new file mode 100644 index 00000000..e34d40c1 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideCommandValidator.cs @@ -0,0 +1,22 @@ +using FluentValidation; +using Streetcode.BLL.MediatR.Media.ArtSlide.Update; +using Streetcode.BLL.Resources; +using Streetcode.BLL.Validators.Media.ArtSlides.Update; + +namespace Streetcode.BLL.Validators.Media.ArtSlides +{ + public class UpdateArtSlideCommandValidator : AbstractValidator + { + public UpdateArtSlideCommandValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(command => command.Dto) + .NotNull() + .WithMessage(ErrorMessages.RequestIsRequired); + + RuleFor(command => command.Dto) + .SetValidator(new UpdateArtSlideDtoValidator()); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideDtoValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideDtoValidator.cs new file mode 100644 index 00000000..59368b2c --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideDtoValidator.cs @@ -0,0 +1,33 @@ +using FluentValidation; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Resources; + +namespace Streetcode.BLL.Validators.Media.ArtSlides.Update +{ + public class UpdateArtSlideDtoValidator : AbstractValidator + { + public UpdateArtSlideDtoValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.Id) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(x => x.Index) + .GreaterThanOrEqualTo(0) + .WithMessage(ErrorMessages.IndexMustBeGreaterOrEqualToZero); + + RuleFor(x => x.TemplateId) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(x => x.StreetcodeId) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleForEach(x => x.ArtSlideItems) + .SetValidator(new ArtSlideItemDtoValidator()); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.BLL/Validators/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDtoValidator.cs b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDtoValidator.cs new file mode 100644 index 00000000..7b49ec96 --- /dev/null +++ b/Streetcode/Streetcode.BLL/Validators/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDtoValidator.cs @@ -0,0 +1,28 @@ +using FluentValidation; +using Streetcode.BLL.DTO.Media.ArtSlidesTemplates; +using Streetcode.BLL.Resources; + +namespace Streetcode.BLL.Validators.Media.ArtSlidesTemplates +{ + public class StreetcodeArtSlideTemplateDtoValidator : AbstractValidator + { + private const int MaxNameLength = 255; + + public StreetcodeArtSlideTemplateDtoValidator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.Id) + .GreaterThan(0) + .WithMessage(ErrorMessages.InvalidId); + + RuleFor(x => x.Name) + .NotEmpty() + .WithMessage(ErrorMessages.NameIsRequired) + .MaximumLength(MaxNameLength) + .WithMessage(string.Format( + ErrorMessages.NameMustNotExceedCharacters, + MaxNameLength)); + } + } +} diff --git a/Streetcode/Streetcode.DAL/Entities/Media/Images/ArtSlideItem.cs b/Streetcode/Streetcode.DAL/Entities/Media/Images/ArtSlideItem.cs new file mode 100644 index 00000000..504ce5df --- /dev/null +++ b/Streetcode/Streetcode.DAL/Entities/Media/Images/ArtSlideItem.cs @@ -0,0 +1,13 @@ +namespace Streetcode.DAL.Entities.Media.Images +{ + public class ArtSlideItem + { + public int Id { get; set; } + public int ArtId { get; set; } + public int SlideId { get; set; } + public int Index { get; set; } + + public Art Art { get; set; } = null!; + public StreetcodeArtSlide Slide { get; set; } = null!; + } +} diff --git a/Streetcode/Streetcode.DAL/Entities/Media/Images/StreetcodeArtSlide.cs b/Streetcode/Streetcode.DAL/Entities/Media/Images/StreetcodeArtSlide.cs new file mode 100644 index 00000000..ef72f54c --- /dev/null +++ b/Streetcode/Streetcode.DAL/Entities/Media/Images/StreetcodeArtSlide.cs @@ -0,0 +1,15 @@ +using Streetcode.DAL.Entities.Streetcode; + +namespace Streetcode.DAL.Entities.Media.Images +{ + public class StreetcodeArtSlide + { + public int Id { get; set; } + public int Index { get; set; } + public int StreetcodeId { get; set; } + + public int TemplateId { get; set; } + public StreetcodeArtSlideTemplate Template { get; set; } = null!; + public List ArtSlideItems { get; set; } = new(); + } +} diff --git a/Streetcode/Streetcode.DAL/Entities/Media/Images/StreetcodeArtSlideTemplate.cs b/Streetcode/Streetcode.DAL/Entities/Media/Images/StreetcodeArtSlideTemplate.cs new file mode 100644 index 00000000..012a4b62 --- /dev/null +++ b/Streetcode/Streetcode.DAL/Entities/Media/Images/StreetcodeArtSlideTemplate.cs @@ -0,0 +1,8 @@ +namespace Streetcode.DAL.Entities.Media.Images +{ + public class StreetcodeArtSlideTemplate + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + } +} diff --git a/Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs b/Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs new file mode 100644 index 00000000..6cf0aca8 --- /dev/null +++ b/Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs @@ -0,0 +1,21 @@ +namespace Streetcode.DAL.Enums +{ + public enum ArtSlideTemplate + { + OneToFourAndFiveToSix = 0, + OneToTwoAndThreeToFourAndFiveToSix = 1, + OneAndTwoAndThreeAndFourAndFiveAndSix = 2, + OneToFour = 3, + OneToTwo = 4, + OneToTwoAndThreeToFour = 5, + OneToFourAndFiveAndSix = 6, + OneAndTwoAndThreeToFour = 7, + OneAndTwoAndThreeAndFour = 8, + OneToTwoAndThreeToFourAndFive = 9, + OneAndTwoAndThreeToFourAndFiveToSix = 10, + OneAndTwoAndThreeToFourAndFive = 11, + OneAndTwoAndThreeToFourAndFiveAndSix = 12, + OneAndTwoAndThreeAndFourAndFive = 13, + + } +} diff --git a/Streetcode/Streetcode.DAL/Persistence/Configurations/ArtSlideItemConfiguration.cs b/Streetcode/Streetcode.DAL/Persistence/Configurations/ArtSlideItemConfiguration.cs new file mode 100644 index 00000000..6a3aeaec --- /dev/null +++ b/Streetcode/Streetcode.DAL/Persistence/Configurations/ArtSlideItemConfiguration.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Streetcode.DAL.Entities.Media.Images; + +namespace Streetcode.DAL.Persistence.Configurations.Media.Images +{ + public class ArtSlideItemConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + + builder.HasOne(x => x.Art) + .WithMany() + .HasForeignKey(x => x.ArtId); + + builder.HasOne(x => x.Slide) + .WithMany(s => s.ArtSlideItems) + .HasForeignKey(x => x.SlideId); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.DAL/Persistence/Configurations/StreetcodeArtSlideConfiguration.cs b/Streetcode/Streetcode.DAL/Persistence/Configurations/StreetcodeArtSlideConfiguration.cs new file mode 100644 index 00000000..445276d5 --- /dev/null +++ b/Streetcode/Streetcode.DAL/Persistence/Configurations/StreetcodeArtSlideConfiguration.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Streetcode.DAL.Entities.Media.Images; + +namespace Streetcode.DAL.Persistence.Configurations +{ + public class StreetcodeArtSlideConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("streetcode_art_slide", "media"); + + builder.HasKey(s => s.Id); + + builder.HasOne(s => s.Template) + .WithMany() + .HasForeignKey(s => s.TemplateId) + .OnDelete(DeleteBehavior.Restrict); + + + builder.HasOne() + .WithMany() + .HasForeignKey(s => s.StreetcodeId) + .OnDelete(DeleteBehavior.Cascade); + + builder.Property(s => s.Index).IsRequired(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.DAL/Persistence/Configurations/StreetcodeArtSlideTemplateConfiguration.cs b/Streetcode/Streetcode.DAL/Persistence/Configurations/StreetcodeArtSlideTemplateConfiguration.cs new file mode 100644 index 00000000..fd62fc72 --- /dev/null +++ b/Streetcode/Streetcode.DAL/Persistence/Configurations/StreetcodeArtSlideTemplateConfiguration.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Enums; + +namespace Streetcode.DAL.Persistence.Configurations +{ + public class StreetcodeArtSlideTemplateConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("streetcode_art_slide_template", "media"); + + builder.HasKey(e => e.Id); + builder.Property(e => e.Id).ValueGeneratedNever(); + builder.Property(e => e.Name).IsRequired().HasMaxLength(100); + + builder.HasData( + Enum.GetValues().Select(e => new StreetcodeArtSlideTemplate + { + Id = (int)e, + Name = e.ToString() + }) + ); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.DAL/Persistence/Migrations/20260613095326_AddArtSlideTemplateTable.Designer.cs b/Streetcode/Streetcode.DAL/Persistence/Migrations/20260613095326_AddArtSlideTemplateTable.Designer.cs new file mode 100644 index 00000000..ffa7ed5f --- /dev/null +++ b/Streetcode/Streetcode.DAL/Persistence/Migrations/20260613095326_AddArtSlideTemplateTable.Designer.cs @@ -0,0 +1,2037 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Streetcode.DAL.Persistence; + +#nullable disable + +namespace Streetcode.DAL.Persistence.Migrations +{ + [DbContext(typeof(StreetcodeDbContext))] + [Migration("20260613095326_AddArtSlideTemplateTable")] + partial class AddArtSlideTemplateTable + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("SQL_Ukrainian_CP1251_CI_AS") + .HasAnnotation("ProductVersion", "6.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + 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") + .HasColumnType("int"); + + 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") + .HasColumnType("int"); + + 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") + .HasColumnType("int"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("int"); + + 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("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Coordinate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CoordinateType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Latitude") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("Longtitude") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("Id"); + + b.ToTable("coordinates", "add_content"); + + b.HasDiscriminator("CoordinateType").HasValue("coordinate_base"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.StreetcodeTagIndex", b => + { + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("TagId") + .HasColumnType("int"); + + b.Property("Index") + .HasColumnType("int"); + + b.Property("IsVisible") + .HasColumnType("bit"); + + b.HasKey("StreetcodeId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("streetcode_tag_index", "add_content"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Subtitle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("SubtitleText") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("StreetcodeId"); + + b.ToTable("subtitles", "add_content"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Title") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("tags", "add_content"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Analytics.StatisticRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Address") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("QrId") + .HasColumnType("int"); + + b.Property("StreetcodeCoordinateId") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("StreetcodeCoordinateId") + .IsUnique(); + + b.HasIndex("StreetcodeId"); + + b.ToTable("qr_coordinates", "coordinates"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Comments.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("ParentCommentId") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ParentCommentId"); + + b.HasIndex("StreetcodeId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Feedback.Response", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("responses", "feedback"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Audio", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("BlobName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Title") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("audios", "media"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.Art", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Title") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId") + .IsUnique(); + + b.ToTable("arts", "media"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.ArtSlideItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ArtId") + .HasColumnType("int"); + + b.Property("Index") + .HasColumnType("int"); + + b.Property("SlideId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ArtId"); + + b.HasIndex("SlideId"); + + b.ToTable("ArtSlideItem"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("BlobName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MimeType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.ToTable("images", "media"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.ImageDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Alt") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Title") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId") + .IsUnique(); + + b.ToTable("image_details", "media"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlide", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Index") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("StreetcodeId"); + + b.HasIndex("TemplateId"); + + b.ToTable("streetcode_art_slide", "media"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlideTemplate", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("streetcode_art_slide_template", "media"); + + b.HasData( + new + { + Id = 0, + Name = "OneToFourAndFiveToSix" + }, + new + { + Id = 1, + Name = "OneToTwoAndThreeToFourAndFiveToSix" + }, + new + { + Id = 2, + Name = "OneAndTwoAndThreeAndFourAndFiveAndSix" + }, + new + { + Id = 3, + Name = "OneToFour" + }, + new + { + Id = 4, + Name = "OneToTwo" + }, + new + { + Id = 5, + Name = "OneToTwoAndThreeToFour" + }, + new + { + Id = 6, + Name = "OneToFourAndFiveAndSix" + }, + new + { + Id = 7, + Name = "OneAndTwoAndThreeToFour" + }, + new + { + Id = 8, + Name = "OneAndTwoAndThreeAndFour" + }, + new + { + Id = 9, + Name = "OneToTwoAndThreeToFourAndFive" + }, + new + { + Id = 10, + Name = "OneAndTwoAndThreeToFourAndFiveToSix" + }, + new + { + Id = 11, + Name = "OneAndTwoAndThreeToFourAndFive" + }, + new + { + Id = 12, + Name = "OneAndTwoAndThreeToFourAndFiveAndSix" + }, + new + { + Id = 13, + Name = "OneAndTwoAndThreeAndFourAndFive" + }); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeImage", b => + { + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.HasKey("ImageId", "StreetcodeId"); + + b.HasIndex("StreetcodeId"); + + b.ToTable("streetcode_image", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Video", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("Title") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Url") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("StreetcodeId"); + + b.ToTable("videos", "media"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.News.News", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("URL") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId") + .IsUnique() + .HasFilter("[ImageId] IS NOT NULL"); + + b.HasIndex("URL") + .IsUnique(); + + b.ToTable("news", "news"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Partners.Partner", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .HasMaxLength(600) + .HasColumnType("nvarchar(600)"); + + b.Property("IsKeyPartner") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsVisibleEverywhere") + .HasColumnType("bit"); + + b.Property("LogoId") + .HasColumnType("int"); + + b.Property("TargetUrl") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("UrlTitle") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("LogoId") + .IsUnique(); + + b.ToTable("partners", "partners"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Partners.PartnerSourceLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("LogoType") + .HasColumnType("tinyint"); + + b.Property("PartnerId") + .HasColumnType("int"); + + b.Property("TargetUrl") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("PartnerId"); + + b.ToTable("partner_source_links", "partners"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Partners.StreetcodePartner", b => + { + b.Property("PartnerId") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.HasKey("PartnerId", "StreetcodeId"); + + b.HasIndex("StreetcodeId"); + + b.ToTable("streetcode_partners", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Sources.SourceLinkCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("source_link_categories", "sources"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Sources.StreetcodeCategoryContent", b => + { + b.Property("SourceLinkCategoryId") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.HasKey("SourceLinkCategoryId", "StreetcodeId"); + + b.HasIndex("StreetcodeId"); + + b.ToTable("streetcode_source_link_categories", "sources"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.RelatedFigure", b => + { + b.Property("ObserverId") + .HasColumnType("int"); + + b.Property("TargetId") + .HasColumnType("int"); + + b.HasKey("ObserverId", "TargetId"); + + b.HasIndex("TargetId"); + + b.ToTable("related_figures", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.StreetcodeArt", b => + { + b.Property("ArtId") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("Index") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1); + + b.HasKey("ArtId", "StreetcodeId"); + + b.HasIndex("StreetcodeId"); + + b.HasIndex("ArtId", "StreetcodeId"); + + b.ToTable("streetcode_art", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Alias") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("AudioId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("DateString") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("EventEndOrPersonDeathDate") + .HasColumnType("datetime2"); + + b.Property("EventStartOrPersonBirthDate") + .HasColumnType("datetime2"); + + b.Property("Index") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StreetcodeType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Teaser") + .HasMaxLength(650) + .HasColumnType("nvarchar(650)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TransliterationUrl") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("AudioId") + .IsUnique() + .HasFilter("[AudioId] IS NOT NULL"); + + b.HasIndex("Index") + .IsUnique(); + + b.HasIndex("TransliterationUrl") + .IsUnique(); + + b.ToTable("streetcodes", "streetcode"); + + b.HasDiscriminator("StreetcodeType").HasValue("streetcode-base"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.TextContent.Fact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("FactContent") + .IsRequired() + .HasMaxLength(600) + .HasColumnType("nvarchar(600)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Index") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("StreetcodeId"); + + b.ToTable("facts", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.TextContent.RelatedTerm", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("TermId") + .HasColumnType("int"); + + b.Property("Word") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("TermId"); + + b.ToTable("related_terms", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.TextContent.Term", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("terms", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.TextContent.Text", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AdditionalText") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("TextContent") + .IsRequired() + .HasMaxLength(15000) + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.HasKey("Id"); + + b.HasIndex("StreetcodeId") + .IsUnique(); + + b.ToTable("texts", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Team.Positions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Position") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("positions", "team"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Team.TeamMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("IsMain") + .HasColumnType("bit"); + + b.Property("LastName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId") + .IsUnique(); + + b.ToTable("team_members", "team"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Team.TeamMemberLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("LogoType") + .HasColumnType("tinyint"); + + b.Property("TargetUrl") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("TeamMemberId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TeamMemberId"); + + b.ToTable("team_member_links", "team"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Team.TeamMemberPositions", b => + { + b.Property("TeamMemberId") + .HasColumnType("int"); + + b.Property("PositionsId") + .HasColumnType("int"); + + b.HasKey("TeamMemberId", "PositionsId"); + + b.HasIndex("PositionsId"); + + b.ToTable("team_member_positions", "team"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Timeline.HistoricalContext", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Title") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("historical_contexts", "timeline"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Timeline.HistoricalContextTimeline", b => + { + b.Property("TimelineId") + .HasColumnType("int"); + + b.Property("HistoricalContextId") + .HasColumnType("int"); + + b.HasKey("TimelineId", "HistoricalContextId"); + + b.HasIndex("HistoricalContextId"); + + b.ToTable("HistoricalContextsTimelines"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Timeline.TimelineItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("DateViewPattern") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(600) + .HasColumnType("nvarchar(600)"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("StreetcodeId"); + + b.ToTable("timeline_items", "timeline"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Toponyms.StreetcodeToponym", b => + { + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("ToponymId") + .HasColumnType("int"); + + b.HasKey("StreetcodeId", "ToponymId"); + + b.HasIndex("ToponymId"); + + b.ToTable("streetcode_toponym", "streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Toponyms.Toponym", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AdminRegionNew") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("AdminRegionOld") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("Community") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("Gromada") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("Oblast") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("StreetName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.Property("StreetType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("toponyms", "toponyms"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Transactions.TransactionLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("UrlTitle") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("StreetcodeId") + .IsUnique(); + + b.ToTable("transaction_links", "transactions"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + 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("RefreshToken") + .HasColumnType("nvarchar(max)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("datetime2"); + + b.Property("Role") + .HasColumnType("int"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Surname") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Types.StreetcodeCoordinate", b => + { + b.HasBaseType("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Coordinate"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.HasIndex("StreetcodeId"); + + b.HasDiscriminator().HasValue("coordinate_streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Types.ToponymCoordinate", b => + { + b.HasBaseType("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Coordinate"); + + b.Property("ToponymId") + .HasColumnType("int"); + + b.HasIndex("ToponymId") + .IsUnique() + .HasFilter("[ToponymId] IS NOT NULL"); + + b.HasDiscriminator().HasValue("coordinate_toponym"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.Types.EventStreetcode", b => + { + b.HasBaseType("Streetcode.DAL.Entities.Streetcode.StreetcodeContent"); + + b.HasDiscriminator().HasValue("streetcode-event"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.Types.PersonStreetcode", b => + { + b.HasBaseType("Streetcode.DAL.Entities.Streetcode.StreetcodeContent"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Rank") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasDiscriminator().HasValue("streetcode-person"); + }); + + 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("Streetcode.DAL.Entities.Users.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Streetcode.DAL.Entities.Users.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("Streetcode.DAL.Entities.Users.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Streetcode.DAL.Entities.Users.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.StreetcodeTagIndex", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("StreetcodeTagIndices") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.AdditionalContent.Tag", "Tag") + .WithMany("StreetcodeTagIndices") + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Streetcode"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Subtitle", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("Subtitles") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Analytics.StatisticRecord", b => + { + b.HasOne("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Types.StreetcodeCoordinate", "StreetcodeCoordinate") + .WithOne("StatisticRecord") + .HasForeignKey("Streetcode.DAL.Entities.Analytics.StatisticRecord", "StreetcodeCoordinateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("StatisticRecords") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Streetcode"); + + b.Navigation("StreetcodeCoordinate"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Comments.Comment", b => + { + b.HasOne("Streetcode.DAL.Entities.Comments.Comment", "ParentComment") + .WithMany("Replies") + .HasForeignKey("ParentCommentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("Comments") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ParentComment"); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.Art", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") + .WithOne("Art") + .HasForeignKey("Streetcode.DAL.Entities.Media.Images.Art", "ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.ArtSlideItem", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Art", "Art") + .WithMany() + .HasForeignKey("ArtId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlide", "Slide") + .WithMany("ArtSlideItems") + .HasForeignKey("SlideId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Art"); + + b.Navigation("Slide"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.ImageDetails", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") + .WithOne("ImageDetails") + .HasForeignKey("Streetcode.DAL.Entities.Media.Images.ImageDetails", "ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlide", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", null) + .WithMany() + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlideTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeImage", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany() + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Video", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("Videos") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.News.News", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") + .WithOne("News") + .HasForeignKey("Streetcode.DAL.Entities.News.News", "ImageId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Partners.Partner", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Logo") + .WithOne("Partner") + .HasForeignKey("Streetcode.DAL.Entities.Partners.Partner", "LogoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Logo"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Partners.PartnerSourceLink", b => + { + b.HasOne("Streetcode.DAL.Entities.Partners.Partner", "Partner") + .WithMany("PartnerSourceLinks") + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Partner"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Partners.StreetcodePartner", b => + { + b.HasOne("Streetcode.DAL.Entities.Partners.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany() + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Partner"); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Sources.SourceLinkCategory", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") + .WithMany("SourceLinkCategories") + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Sources.StreetcodeCategoryContent", b => + { + b.HasOne("Streetcode.DAL.Entities.Sources.SourceLinkCategory", "SourceLinkCategory") + .WithMany("StreetcodeCategoryContents") + .HasForeignKey("SourceLinkCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("StreetcodeCategoryContents") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SourceLinkCategory"); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.RelatedFigure", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Observer") + .WithMany("Observers") + .HasForeignKey("ObserverId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Target") + .WithMany("Targets") + .HasForeignKey("TargetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Observer"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.StreetcodeArt", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Art", "Art") + .WithMany("StreetcodeArts") + .HasForeignKey("ArtId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("StreetcodeArts") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Art"); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Audio", "Audio") + .WithOne("Streetcode") + .HasForeignKey("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "AudioId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Audio"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.TextContent.Fact", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") + .WithMany("Facts") + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("Facts") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.TextContent.RelatedTerm", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.TextContent.Term", "Term") + .WithMany("RelatedTerms") + .HasForeignKey("TermId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Term"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.TextContent.Text", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithOne("Text") + .HasForeignKey("Streetcode.DAL.Entities.Streetcode.TextContent.Text", "StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Team.TeamMember", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") + .WithOne("TeamMember") + .HasForeignKey("Streetcode.DAL.Entities.Team.TeamMember", "ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Team.TeamMemberLink", b => + { + b.HasOne("Streetcode.DAL.Entities.Team.TeamMember", "TeamMember") + .WithMany("TeamMemberLinks") + .HasForeignKey("TeamMemberId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TeamMember"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Team.TeamMemberPositions", b => + { + b.HasOne("Streetcode.DAL.Entities.Team.Positions", "Positions") + .WithMany() + .HasForeignKey("PositionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Team.TeamMember", "TeamMember") + .WithMany() + .HasForeignKey("TeamMemberId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Positions"); + + b.Navigation("TeamMember"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Timeline.HistoricalContextTimeline", b => + { + b.HasOne("Streetcode.DAL.Entities.Timeline.HistoricalContext", "HistoricalContext") + .WithMany("HistoricalContextTimelines") + .HasForeignKey("HistoricalContextId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Timeline.TimelineItem", "Timeline") + .WithMany("HistoricalContextTimelines") + .HasForeignKey("TimelineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HistoricalContext"); + + b.Navigation("Timeline"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Timeline.TimelineItem", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("TimelineItems") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Toponyms.StreetcodeToponym", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany() + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Toponyms.Toponym", "Toponym") + .WithMany() + .HasForeignKey("ToponymId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Streetcode"); + + b.Navigation("Toponym"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Transactions.TransactionLink", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithOne("TransactionLink") + .HasForeignKey("Streetcode.DAL.Entities.Transactions.TransactionLink", "StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Types.StreetcodeCoordinate", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", "Streetcode") + .WithMany("Coordinates") + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Types.ToponymCoordinate", b => + { + b.HasOne("Streetcode.DAL.Entities.Toponyms.Toponym", "Toponym") + .WithOne("Coordinate") + .HasForeignKey("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Types.ToponymCoordinate", "ToponymId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Toponym"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Tag", b => + { + b.Navigation("StreetcodeTagIndices"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Comments.Comment", b => + { + b.Navigation("Replies"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Audio", b => + { + b.Navigation("Streetcode"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.Art", b => + { + b.Navigation("StreetcodeArts"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.Image", b => + { + b.Navigation("Art"); + + b.Navigation("Facts"); + + b.Navigation("ImageDetails"); + + b.Navigation("News"); + + b.Navigation("Partner"); + + b.Navigation("SourceLinkCategories"); + + b.Navigation("TeamMember"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlide", b => + { + b.Navigation("ArtSlideItems"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Partners.Partner", b => + { + b.Navigation("PartnerSourceLinks"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Sources.SourceLinkCategory", b => + { + b.Navigation("StreetcodeCategoryContents"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", b => + { + b.Navigation("Comments"); + + b.Navigation("Coordinates"); + + b.Navigation("Facts"); + + b.Navigation("Observers"); + + b.Navigation("StatisticRecords"); + + b.Navigation("StreetcodeArts"); + + b.Navigation("StreetcodeCategoryContents"); + + b.Navigation("StreetcodeTagIndices"); + + b.Navigation("Subtitles"); + + b.Navigation("Targets"); + + b.Navigation("Text"); + + b.Navigation("TimelineItems"); + + b.Navigation("TransactionLink"); + + b.Navigation("Videos"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Streetcode.TextContent.Term", b => + { + b.Navigation("RelatedTerms"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Team.TeamMember", b => + { + b.Navigation("TeamMemberLinks"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Timeline.HistoricalContext", b => + { + b.Navigation("HistoricalContextTimelines"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Timeline.TimelineItem", b => + { + b.Navigation("HistoricalContextTimelines"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Toponyms.Toponym", b => + { + b.Navigation("Coordinate") + .IsRequired(); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.AdditionalContent.Coordinates.Types.StreetcodeCoordinate", b => + { + b.Navigation("StatisticRecord") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Streetcode/Streetcode.DAL/Persistence/Migrations/20260613095326_AddArtSlideTemplateTable.cs b/Streetcode/Streetcode.DAL/Persistence/Migrations/20260613095326_AddArtSlideTemplateTable.cs new file mode 100644 index 00000000..1a9d622e --- /dev/null +++ b/Streetcode/Streetcode.DAL/Persistence/Migrations/20260613095326_AddArtSlideTemplateTable.cs @@ -0,0 +1,142 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Streetcode.DAL.Persistence.Migrations +{ + public partial class AddArtSlideTemplateTable : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "streetcode_art_slide_template", + schema: "media", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_streetcode_art_slide_template", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "streetcode_art_slide", + schema: "media", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Index = table.Column(type: "int", nullable: false), + StreetcodeId = table.Column(type: "int", nullable: false), + TemplateId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_streetcode_art_slide", x => x.Id); + table.ForeignKey( + name: "FK_streetcode_art_slide_streetcode_art_slide_template_TemplateId", + column: x => x.TemplateId, + principalSchema: "media", + principalTable: "streetcode_art_slide_template", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_streetcode_art_slide_streetcodes_StreetcodeId", + column: x => x.StreetcodeId, + principalSchema: "streetcode", + principalTable: "streetcodes", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ArtSlideItem", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ArtId = table.Column(type: "int", nullable: false), + SlideId = table.Column(type: "int", nullable: false), + Index = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ArtSlideItem", x => x.Id); + table.ForeignKey( + name: "FK_ArtSlideItem_arts_ArtId", + column: x => x.ArtId, + principalSchema: "media", + principalTable: "arts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ArtSlideItem_streetcode_art_slide_SlideId", + column: x => x.SlideId, + principalSchema: "media", + principalTable: "streetcode_art_slide", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + schema: "media", + table: "streetcode_art_slide_template", + columns: new[] { "Id", "Name" }, + values: new object[,] + { + { 0, "OneToFourAndFiveToSix" }, + { 1, "OneToTwoAndThreeToFourAndFiveToSix" }, + { 2, "OneAndTwoAndThreeAndFourAndFiveAndSix" }, + { 3, "OneToFour" }, + { 4, "OneToTwo" }, + { 5, "OneToTwoAndThreeToFour" }, + { 6, "OneToFourAndFiveAndSix" }, + { 7, "OneAndTwoAndThreeToFour" }, + { 8, "OneAndTwoAndThreeAndFour" }, + { 9, "OneToTwoAndThreeToFourAndFive" }, + { 10, "OneAndTwoAndThreeToFourAndFiveToSix" }, + { 11, "OneAndTwoAndThreeToFourAndFive" }, + { 12, "OneAndTwoAndThreeToFourAndFiveAndSix" }, + { 13, "OneAndTwoAndThreeAndFourAndFive" } + }); + + migrationBuilder.CreateIndex( + name: "IX_ArtSlideItem_ArtId", + table: "ArtSlideItem", + column: "ArtId"); + + migrationBuilder.CreateIndex( + name: "IX_ArtSlideItem_SlideId", + table: "ArtSlideItem", + column: "SlideId"); + + migrationBuilder.CreateIndex( + name: "IX_streetcode_art_slide_StreetcodeId", + schema: "media", + table: "streetcode_art_slide", + column: "StreetcodeId"); + + migrationBuilder.CreateIndex( + name: "IX_streetcode_art_slide_TemplateId", + schema: "media", + table: "streetcode_art_slide", + column: "TemplateId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ArtSlideItem"); + + migrationBuilder.DropTable( + name: "streetcode_art_slide", + schema: "media"); + + migrationBuilder.DropTable( + name: "streetcode_art_slide_template", + schema: "media"); + } + } +} diff --git a/Streetcode/Streetcode.DAL/Persistence/Migrations/StreetcodeDbContextModelSnapshot.cs b/Streetcode/Streetcode.DAL/Persistence/Migrations/StreetcodeDbContextModelSnapshot.cs index 0a6b0318..cdcbe3f1 100644 --- a/Streetcode/Streetcode.DAL/Persistence/Migrations/StreetcodeDbContextModelSnapshot.cs +++ b/Streetcode/Streetcode.DAL/Persistence/Migrations/StreetcodeDbContextModelSnapshot.cs @@ -396,6 +396,32 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("arts", "media"); }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.ArtSlideItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ArtId") + .HasColumnType("int"); + + b.Property("Index") + .HasColumnType("int"); + + b.Property("SlideId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ArtId"); + + b.HasIndex("SlideId"); + + b.ToTable("ArtSlideItem"); + }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.Image", b => { b.Property("Id") @@ -446,6 +472,119 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("image_details", "media"); }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlide", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Index") + .HasColumnType("int"); + + b.Property("StreetcodeId") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("StreetcodeId"); + + b.HasIndex("TemplateId"); + + b.ToTable("streetcode_art_slide", "media"); + }); + + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlideTemplate", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("streetcode_art_slide_template", "media"); + + b.HasData( + new + { + Id = 0, + Name = "OneToFourAndFiveToSix" + }, + new + { + Id = 1, + Name = "OneToTwoAndThreeToFourAndFiveToSix" + }, + new + { + Id = 2, + Name = "OneAndTwoAndThreeAndFourAndFiveAndSix" + }, + new + { + Id = 3, + Name = "OneToFour" + }, + new + { + Id = 4, + Name = "OneToTwo" + }, + new + { + Id = 5, + Name = "OneToTwoAndThreeToFour" + }, + new + { + Id = 6, + Name = "OneToFourAndFiveAndSix" + }, + new + { + Id = 7, + Name = "OneAndTwoAndThreeToFour" + }, + new + { + Id = 8, + Name = "OneAndTwoAndThreeAndFour" + }, + new + { + Id = 9, + Name = "OneToTwoAndThreeToFourAndFive" + }, + new + { + Id = 10, + Name = "OneAndTwoAndThreeToFourAndFiveToSix" + }, + new + { + Id = 11, + Name = "OneAndTwoAndThreeToFourAndFive" + }, + new + { + Id = 12, + Name = "OneAndTwoAndThreeToFourAndFiveAndSix" + }, + new + { + Id = 13, + Name = "OneAndTwoAndThreeAndFourAndFive" + }); + }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeImage", b => { b.Property("ImageId") @@ -1409,6 +1548,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Image"); }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.ArtSlideItem", b => + { + b.HasOne("Streetcode.DAL.Entities.Media.Images.Art", "Art") + .WithMany() + .HasForeignKey("ArtId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlide", "Slide") + .WithMany("ArtSlideItems") + .HasForeignKey("SlideId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Art"); + + b.Navigation("Slide"); + }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.ImageDetails", b => { b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") @@ -1420,6 +1578,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Image"); }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlide", b => + { + b.HasOne("Streetcode.DAL.Entities.Streetcode.StreetcodeContent", null) + .WithMany() + .HasForeignKey("StreetcodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlideTemplate", "Template") + .WithMany() + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Template"); + }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeImage", b => { b.HasOne("Streetcode.DAL.Entities.Media.Images.Image", "Image") @@ -1777,6 +1952,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("TeamMember"); }); + modelBuilder.Entity("Streetcode.DAL.Entities.Media.Images.StreetcodeArtSlide", b => + { + b.Navigation("ArtSlideItems"); + }); + modelBuilder.Entity("Streetcode.DAL.Entities.Partners.Partner", b => { b.Navigation("PartnerSourceLinks"); diff --git a/Streetcode/Streetcode.DAL/Persistence/StreetcodeDbContext.cs b/Streetcode/Streetcode.DAL/Persistence/StreetcodeDbContext.cs index 49644406..5fe35e60 100644 --- a/Streetcode/Streetcode.DAL/Persistence/StreetcodeDbContext.cs +++ b/Streetcode/Streetcode.DAL/Persistence/StreetcodeDbContext.cs @@ -69,6 +69,8 @@ public StreetcodeDbContext(DbContextOptions options) public DbSet HistoricalContextsTimelines { get; set; } = null!; public DbSet StreetcodePartners { get; set; } = null!; public DbSet TeamMemberPosition { get; set; } = null!; + public DbSet StreetcodeArtSlides { get; set; } = null!; + public DbSet StreetcodeArtSlideTemplateConfiguration { get; set; } = null!; protected override void OnModelCreating(ModelBuilder builder) { @@ -115,5 +117,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.ApplyConfiguration(new ResponseConfiguration()); builder.ApplyConfiguration(new ArtConfiguration()); builder.ApplyConfiguration(new CommentConfiguration()); + builder.ApplyConfiguration(new StreetcodeArtSlideTemplateConfiguration()); + builder.ApplyConfiguration(new StreetcodeArtSlideConfiguration()); } } diff --git a/Streetcode/Streetcode.DAL/Repositories/Interfaces/Base/IRepositoryWrapper.cs b/Streetcode/Streetcode.DAL/Repositories/Interfaces/Base/IRepositoryWrapper.cs index c7277438..e5cd9cd4 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Interfaces/Base/IRepositoryWrapper.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Interfaces/Base/IRepositoryWrapper.cs @@ -59,4 +59,8 @@ public interface IRepositoryWrapper public Task SaveChangesAsync(CancellationToken cancellationToken = default); public TransactionScope BeginTransaction(); + + IStreetcodeArtSlideRepository StreetcodeArtSlideRepository { get; } + IStreetcodeArtSlideTemplateRepository StreetcodeArtSlideTemplateRepository { get; } + IArtSlideItemRepository ArtSlideItemRepository { get; } } diff --git a/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IArtSlideItemRepository.cs b/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IArtSlideItemRepository.cs new file mode 100644 index 00000000..ee45343e --- /dev/null +++ b/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IArtSlideItemRepository.cs @@ -0,0 +1,9 @@ +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.DAL.Repositories.Interfaces.Media.Images +{ + public interface IArtSlideItemRepository : IRepositoryBase + { + } +} diff --git a/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IStreetcodeArtSlideRepository.cs b/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IStreetcodeArtSlideRepository.cs new file mode 100644 index 00000000..e9fb616e --- /dev/null +++ b/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IStreetcodeArtSlideRepository.cs @@ -0,0 +1,9 @@ +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.DAL.Repositories.Interfaces.Media.Images +{ + public interface IStreetcodeArtSlideRepository : IRepositoryBase + { + } +} diff --git a/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IStreetcodeArtSlideTemplateRepository.cs b/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IStreetcodeArtSlideTemplateRepository.cs new file mode 100644 index 00000000..e5c01d75 --- /dev/null +++ b/Streetcode/Streetcode.DAL/Repositories/Interfaces/Media/Images/IStreetcodeArtSlideTemplateRepository.cs @@ -0,0 +1,9 @@ +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; + +namespace Streetcode.DAL.Repositories.Interfaces.Media.Images +{ + public interface IStreetcodeArtSlideTemplateRepository : IRepositoryBase + { + } +} diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs index 7d866a8d..043dd15e 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs @@ -1,4 +1,4 @@ -using System.Transactions; +using Microsoft.EntityFrameworkCore; using Repositories.Interfaces; using Streetcode.DAL.Persistence; using Streetcode.DAL.Repositories.Interfaces.AdditionalContent; @@ -20,6 +20,7 @@ using Streetcode.DAL.Repositories.Realizations.Analytics; using Streetcode.DAL.Repositories.Realizations.Comments; using Streetcode.DAL.Repositories.Realizations.Media; +using Streetcode.DAL.Repositories.Realizations.Media.Art; using Streetcode.DAL.Repositories.Realizations.Media.Images; using Streetcode.DAL.Repositories.Realizations.Newss; using Streetcode.DAL.Repositories.Realizations.Partners; @@ -31,6 +32,7 @@ using Streetcode.DAL.Repositories.Realizations.Toponyms; using Streetcode.DAL.Repositories.Realizations.Transactions; using Streetcode.DAL.Repositories.Realizations.Users; +using System.Transactions; namespace Streetcode.DAL.Repositories.Realizations.Base; @@ -110,6 +112,10 @@ public class RepositoryWrapper : IRepositoryWrapper private ICommentRepository? _commentRepository; + private IStreetcodeArtSlideRepository? _streetcodeArtSlideRepository; + private IStreetcodeArtSlideTemplateRepository? _streetcodeArtSlideTemplateRepository; + private IArtSlideItemRepository _artSlideItemRepository; + public RepositoryWrapper(StreetcodeDbContext streetcodeDbContext) { _streetcodeDbContext = streetcodeDbContext; @@ -586,4 +592,40 @@ public TransactionScope BeginTransaction() { return new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); } + + public IStreetcodeArtSlideRepository StreetcodeArtSlideRepository + { + get + { + if (_streetcodeArtSlideRepository is null) + { + _streetcodeArtSlideRepository = new StreetcodeArtSlideRepository(_streetcodeDbContext); + } + return _streetcodeArtSlideRepository; + } + } + + public IStreetcodeArtSlideTemplateRepository StreetcodeArtSlideTemplateRepository + { + get + { + if (_streetcodeArtSlideTemplateRepository is null) + { + _streetcodeArtSlideTemplateRepository = new StreetcodeArtSlideTemplateRepository(_streetcodeDbContext); + } + return _streetcodeArtSlideTemplateRepository; + } + } + public IArtSlideItemRepository ArtSlideItemRepository + { + get + { + if (_artSlideItemRepository is null) + { + _artSlideItemRepository = new ArtSlideItemRepository(_streetcodeDbContext); + } + return _artSlideItemRepository; + } + } + } diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/ArtRepository.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/ArtRepository.cs index 3fbf5612..b186db42 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/ArtRepository.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/ArtRepository.cs @@ -1,11 +1,10 @@ using Repositories.Interfaces; -using Streetcode.DAL.Entities.Media.Images; using Streetcode.DAL.Persistence; using Streetcode.DAL.Repositories.Realizations.Base; namespace Streetcode.DAL.Repositories.Realizations.Media.Images; -public class ArtRepository : RepositoryBase, IArtRepository +public class ArtRepository : RepositoryBase, IArtRepository { public ArtRepository(StreetcodeDbContext dbContext) : base(dbContext) diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/ArtSlideItemRepository.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/ArtSlideItemRepository.cs new file mode 100644 index 00000000..80b8865d --- /dev/null +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/ArtSlideItemRepository.cs @@ -0,0 +1,14 @@ +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Persistence; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using Streetcode.DAL.Repositories.Realizations.Base; + +namespace Streetcode.DAL.Repositories.Realizations.Media.Images +{ + public class ArtSlideItemRepository : RepositoryBase, IArtSlideItemRepository + { + public ArtSlideItemRepository(StreetcodeDbContext context) : base(context) + { + } + } +} diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/StreetcodeArtSlideRepository.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/StreetcodeArtSlideRepository.cs new file mode 100644 index 00000000..c230a19e --- /dev/null +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/StreetcodeArtSlideRepository.cs @@ -0,0 +1,15 @@ +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Persistence; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using Streetcode.DAL.Repositories.Realizations.Base; + +namespace Streetcode.DAL.Repositories.Realizations.Media.Images +{ + public class StreetcodeArtSlideRepository : RepositoryBase, IStreetcodeArtSlideRepository + { + public StreetcodeArtSlideRepository(StreetcodeDbContext dbContext) + : base(dbContext) + { + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/StreetcodeArtSlideTemplateRepository.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/StreetcodeArtSlideTemplateRepository.cs new file mode 100644 index 00000000..f439ddb7 --- /dev/null +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Media/Images/StreetcodeArtSlideTemplateRepository.cs @@ -0,0 +1,15 @@ +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Persistence; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using Streetcode.DAL.Repositories.Realizations.Base; + +namespace Streetcode.DAL.Repositories.Realizations.Media.Art +{ + public class StreetcodeArtSlideTemplateRepository : RepositoryBase, IStreetcodeArtSlideTemplateRepository + { + public StreetcodeArtSlideTemplateRepository(StreetcodeDbContext dbContext) + : base(dbContext) + { + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideController.cs b/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideController.cs new file mode 100644 index 00000000..283bcb6e --- /dev/null +++ b/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideController.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Mvc; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.MediatR.Media.ArtSlide.Create; +using Streetcode.BLL.MediatR.Media.ArtSlide.CreateAll; +using Streetcode.BLL.MediatR.Media.ArtSlide.Delete; +using Streetcode.BLL.MediatR.Media.ArtSlide.GetAllByStreetcodeId; +using Streetcode.BLL.MediatR.Media.ArtSlide.Update; +using Streetcode.DAL.Enums; +using Streetcode.WebApi.Attributes; + +namespace Streetcode.WebApi.Controllers.Media +{ + [ApiController] + [Route("api/[controller]")] + public sealed class ArtSlideController : BaseApiController + { + [HttpGet("{streetcodeId:int}")] + public async Task GetAllByStreetcodeId([FromRoute] int streetcodeId, CancellationToken cancellationToken = default) + { + return base.HandleResult( + await base.Mediator.Send(new GetAllArtSlidesByStreetcodeIdQuery(streetcodeId), cancellationToken) + ); + } + + [HttpPost] + [AuthorizeRoles(UserRole.MainAdministrator)] + public async Task Create([FromBody] CreateStreetcodeArtSlideDto dto, CancellationToken cancellationToken = default) + { + return base.HandleResult( + await base.Mediator.Send(new CreateArtSlideCommand(dto), cancellationToken) + ); + } + + [HttpPost("CreateAll")] + //[AuthorizeRoles(UserRole.MainAdministrator)] + public async Task CreateAll([FromBody] List dtos, CancellationToken cancellationToken = default) + { + return base.HandleResult( + await base.Mediator.Send(new CreateAllArtSlidesCommand(dtos), cancellationToken) + ); + } + + [HttpPut] + [AuthorizeRoles(UserRole.MainAdministrator)] + public async Task Update([FromBody] UpdateArtSlideDto dto, CancellationToken cancellationToken = default) + { + return base.HandleResult( + await base.Mediator.Send(new UpdateArtSlideCommand(dto), cancellationToken) + ); + } + + [HttpDelete("{id:int}")] + [AuthorizeRoles(UserRole.MainAdministrator)] + public async Task Delete([FromRoute] int id, CancellationToken cancellationToken = default) + { + return base.HandleResult( + await base.Mediator.Send(new DeleteArtSlideCommand(id), cancellationToken) + ); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideTeamplatesController.cs b/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideTeamplatesController.cs new file mode 100644 index 00000000..c487bbde --- /dev/null +++ b/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideTeamplatesController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc; +using Streetcode.BLL.MediatR.Media.ArtSlideTeamplates.GetAll; + +namespace Streetcode.WebApi.Controllers.Media +{ + [ApiController] + [Route("api/[controller]")] + public class ArtSlideTeamplatesController : BaseApiController + { + [HttpGet] + public async Task GetAll(CancellationToken cancellationToken = default) + { + return base.HandleResult( + await base.Mediator.Send(new GetAllArtSlideTemplatesQuery(), cancellationToken) + ); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs b/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs index a0d4bf3f..a2d6c162 100644 --- a/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs +++ b/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs @@ -1,8 +1,15 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Mvc; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.MediatR.Media.Art.Create; +using Streetcode.BLL.MediatR.Media.Art.Delete; using Streetcode.BLL.MediatR.Media.Art.GetAll; using Streetcode.BLL.MediatR.Media.Art.GetById; using Streetcode.BLL.MediatR.Media.Art.GetByStreetcodeId; +using Streetcode.BLL.MediatR.Media.Art.Update; +using Streetcode.DAL.Enums; +using Streetcode.WebApi.Attributes; + namespace Streetcode.WebApi.Controllers.Media.Images; @@ -32,4 +39,32 @@ public async Task GetByStreetcodeId([FromRoute] int streetcodeId, await base.Mediator.Send(new GetArtsByStreetcodeIdQuery(streetcodeId), cancellationToken) ); } + + [HttpPost] + [AuthorizeRoles(UserRole.MainAdministrator)] + public async Task Create([FromBody] ArtCreateDto art, CancellationToken cancellationToken = default) + { + return base.HandleResult( + await base.Mediator.Send(new CreateArtCommand(art), cancellationToken) + ); + } + + [HttpPut("{id:int}")] + [AuthorizeRoles(UserRole.MainAdministrator)] + public async Task Update([FromRoute] int id, [FromBody] ArtUpdateDto art, CancellationToken cancellationToken = default) + { + art.Id = id; + return base.HandleResult( + await base.Mediator.Send(new UpdateArtCommand(art), cancellationToken) + ); + } + + [HttpDelete("{id:int}")] + [AuthorizeRoles(UserRole.MainAdministrator)] + public async Task Delete([FromRoute] int id, CancellationToken cancellationToken = default) + { + return base.HandleResult( + await base.Mediator.Send(new DeleteArtCommand(id), cancellationToken) + ); + } } \ No newline at end of file diff --git a/Streetcode/Streetcode.WebApi/Extensions/SeedingLocalExtension.cs b/Streetcode/Streetcode.WebApi/Extensions/SeedingLocalExtension.cs index 45b489cb..7fe5e6bd 100644 --- a/Streetcode/Streetcode.WebApi/Extensions/SeedingLocalExtension.cs +++ b/Streetcode/Streetcode.WebApi/Extensions/SeedingLocalExtension.cs @@ -101,74 +101,69 @@ public static async Task SeedDataAsync(this WebApplication app) await TeamMemberLinksSeeder.FillSeedAsync(dbContext); } + await PersonStreetcodeSeeder.FillSeedAsync(dbContext); - if (!await dbContext.Audios.AnyAsync() && !await dbContext.Streetcodes.AnyAsync()) - { - await PersonStreetcodeSeeder.FillSeedAsync(dbContext); + await SubtitlesSeeder.FillSeedAsync(dbContext); - await SubtitlesSeeder.FillSeedAsync(dbContext); + await StreetcodeCoordinatesSeeder.FillSeedAsync(dbContext); - await StreetcodeCoordinatesSeeder.FillSeedAsync(dbContext); + await VideosSeeder.FillSeedAsync(dbContext); - await VideosSeeder.FillSeedAsync(dbContext); + if (!await dbContext.Partners.AnyAsync()) + { + await PartnersSeeder.FillSeedAsync(dbContext); - if (!await dbContext.Partners.AnyAsync()) - { - await PartnersSeeder.FillSeedAsync(dbContext); + await PartnerSourceLinksSeeder.FillSeedAsync(dbContext); - await PartnerSourceLinksSeeder.FillSeedAsync(dbContext); + await StreetcodePartnersSeeder.FillSeedAsync(dbContext); + } - await StreetcodePartnersSeeder.FillSeedAsync(dbContext); - } + if (!await dbContext.Arts.AnyAsync()) + { + await ArtsSeeder.FillSeedAsync(dbContext); - if (!await dbContext.Arts.AnyAsync()) - { - await ArtsSeeder.FillSeedAsync(dbContext); + await StreetcodeArtsSeeder.FillSeedAsync(dbContext); + } - await StreetcodeArtsSeeder.FillSeedAsync(dbContext); - } + await TextsSeeder.FillSeedAsync(dbContext); - await TextsSeeder.FillSeedAsync(dbContext); + if (!await dbContext.TimelineItems.AnyAsync()) + { + await TimelineItemsSeeder.FillSeedAsync(dbContext); - if (!await dbContext.TimelineItems.AnyAsync()) + if (!await dbContext.HistoricalContexts.AnyAsync()) { - await TimelineItemsSeeder.FillSeedAsync(dbContext); - - if (!await dbContext.HistoricalContexts.AnyAsync()) - { - await HistoricalContextsSeeder.FillSeedAsync(dbContext); + await HistoricalContextsSeeder.FillSeedAsync(dbContext); - await HistoricalContextsTimelinesSeeder.FillSeedAsync(dbContext); - } + await HistoricalContextsTimelinesSeeder.FillSeedAsync(dbContext); } + } - await TransactionLinkSeeder.FillSeedAsync(dbContext); + await TransactionLinkSeeder.FillSeedAsync(dbContext); - if (!await dbContext.Facts.AnyAsync()) - { - await FactsSeeder.FillSeedAsync(dbContext); - await ImageDetailsesSeeder.FillSeedAsync(dbContext); - } + if (!await dbContext.Facts.AnyAsync()) + { + await FactsSeeder.FillSeedAsync(dbContext); + await ImageDetailsesSeeder.FillSeedAsync(dbContext); + } - if (!await dbContext.SourceLinks.AnyAsync()) - { - await SourceLinkCategorySeeder.FillSeedAsync(dbContext); + if (!await dbContext.SourceLinks.AnyAsync()) + { + await SourceLinkCategorySeeder.FillSeedAsync(dbContext); - await StreetcodeCategoryContentSeeder.FillSeedAsync(dbContext); - } + await StreetcodeCategoryContentSeeder.FillSeedAsync(dbContext); + } - await RelatedFiguresSeeder.FillSeedAsync(dbContext); + await RelatedFiguresSeeder.FillSeedAsync(dbContext); - await StreetcodeImagesSeeder.FillSeedAsync(dbContext); + await StreetcodeImagesSeeder.FillSeedAsync(dbContext); - if (!await dbContext.Tags.AnyAsync()) - { - await TagsSeeder.FillSeedAsync(dbContext); + if (!await dbContext.Tags.AnyAsync()) + { + await TagsSeeder.FillSeedAsync(dbContext); - await StreetcodeTagIndexSeeder.FillSeedAsync(dbContext); - } + await StreetcodeTagIndexSeeder.FillSeedAsync(dbContext); } - await dbContext.SaveChangesAsync(); } } diff --git a/Streetcode/Streetcode.WebApi/Program.cs b/Streetcode/Streetcode.WebApi/Program.cs index e724d10f..61f713e4 100644 --- a/Streetcode/Streetcode.WebApi/Program.cs +++ b/Streetcode/Streetcode.WebApi/Program.cs @@ -42,7 +42,7 @@ await app.ApplyMigrations(); -// await app.SeedDataAsync(); // uncomment for seeding data in local +await app.SeedDataAsync(); // uncomment for seeding data in local app.UseCors(); app.UseCustomMiddlewares(); diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Create/CreateArtHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Create/CreateArtHandlerTests.cs new file mode 100644 index 00000000..7bed25f3 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Create/CreateArtHandlerTests.cs @@ -0,0 +1,73 @@ +using AutoMapper; +using Moq; +using Repositories.Interfaces; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.MediatR.Media.Art.Create; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Xunit; + +using ArtEntity = Streetcode.DAL.Entities.Media.Images.Art; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.Art.Create; + +public class CreateArtHandlerTests +{ + private readonly CreateArtHandler _handler; + private readonly Mock _mockRepository; + private readonly Mock _mockArtRepository; + + public CreateArtHandlerTests() + { + _mockRepository = new Mock(); + _mockArtRepository = new Mock(); + + _mockRepository + .Setup(r => r.ArtRepository) + .Returns(_mockArtRepository.Object); + + var mapper = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + }).CreateMapper(); + + _handler = new CreateArtHandler( + mapper, + _mockRepository.Object); + } + + [Fact] + public async Task Handle_ValidCommand_ReturnCreatedArt() + { + // Arrange + const string expectedTitle = "Test Art"; + + var artDto = new ArtCreateDto + { + Title = expectedTitle, + }; + + var command = new CreateArtCommand(artDto); + + _mockArtRepository + .Setup(r => r.CreateAsync(It.IsAny())) + .ReturnsAsync(new ArtEntity()); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + Assert.True(result.IsSuccess); + Assert.NotNull(result.Value); + Assert.Equal(expectedTitle, result.Value.Title); + Assert.IsType(result.Value); + + _mockArtRepository.Verify( + r => r.CreateAsync(It.IsAny()), + Times.Once); + + _mockRepository.Verify( + r => r.SaveChangesAsync(), + Times.Once); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs new file mode 100644 index 00000000..1e3bdd4f --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs @@ -0,0 +1,99 @@ +using System.Linq.Expressions; +using MediatR; +using Moq; +using Repositories.Interfaces; +using Streetcode.BLL.MediatR.Media.Art.Delete; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Xunit; + +using ArtEntity = Streetcode.DAL.Entities.Media.Images.Art; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.Art.Delete; + +public class DeleteArtHandlerTests +{ + private readonly DeleteArtHandler _handler; + private readonly Mock _mockRepository; + private readonly Mock _mockArtRepository; + + public DeleteArtHandlerTests() + { + _mockRepository = new Mock(); + _mockArtRepository = new Mock(); + + _mockRepository + .Setup(r => r.ArtRepository) + .Returns(_mockArtRepository.Object); + + _handler = new DeleteArtHandler(_mockRepository.Object); + } + + [Fact] + public async Task Handle_ArtExists_DeleteArt() + { + // Arrange + const int artId = 1; + + var art = new ArtEntity + { + Id = artId, + Title = "Test Art", + }; + + _mockArtRepository + .Setup(r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + null)) + .ReturnsAsync(art); + + var command = new DeleteArtCommand(artId); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + Assert.True(result.IsSuccess); + Assert.Equal(Unit.Value, result.Value); + + _mockArtRepository.Verify( + r => r.Delete(art), + Times.Once); + + _mockRepository.Verify( + r => r.SaveChangesAsync(It.IsAny()), + Times.Once); + } + + [Fact] + public async Task Handle_ArtNotFound_ReturnError() + { + // Arrange + const int artId = 1; + + _mockArtRepository + .Setup(r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + null)) + .ReturnsAsync((ArtEntity?)null); + + var command = new DeleteArtCommand(artId); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + Assert.True(result.IsFailed); + Assert.Equal( + string.Format(ErrorMessages.EntityNotFound, artId), + result.Errors.First().Message); + + _mockArtRepository.Verify( + r => r.Delete(It.IsAny()), + Times.Never); + + _mockRepository.Verify( + r => r.SaveChangesAsync(It.IsAny()), + Times.Never); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs new file mode 100644 index 00000000..3dfd500d --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs @@ -0,0 +1,129 @@ +using System.Linq.Expressions; +using AutoMapper; +using Microsoft.EntityFrameworkCore.Query; +using Moq; +using Repositories.Interfaces; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.MediatR.Media.Art.Update; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Xunit; + +using ArtEntity = Streetcode.DAL.Entities.Media.Images.Art; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.Art.Update; + +public class UpdateArtHandlerTests +{ + private readonly UpdateArtHandler _handler; + private readonly Mock _mockRepository; + private readonly Mock _mockArtRepository; + + public UpdateArtHandlerTests() + { + _mockRepository = new Mock(); + _mockArtRepository = new Mock(); + + _mockRepository + .Setup(r => r.ArtRepository) + .Returns(_mockArtRepository.Object); + + var mapper = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); + }).CreateMapper(); + + _handler = new UpdateArtHandler( + mapper, + _mockRepository.Object); + } + + [Fact] + public async Task Handle_ArtExists_ReturnUpdatedArt() + { + // Arrange + const int artId = 1; + const string updatedTitle = "Updated Art"; + + var art = new ArtEntity + { + Id = artId, + Title = "Old Title", + Description = "Old Description", + ImageId = 1, + }; + + var updateDto = new ArtUpdateDto + { + Id = artId, + Title = updatedTitle, + Description = "Updated Description", + ImageId = 1, + }; + + _mockArtRepository + .Setup(r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>>())) + .ReturnsAsync(art); + + var command = new UpdateArtCommand(updateDto); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + Assert.True(result.IsSuccess); + Assert.NotNull(result.Value); + Assert.Equal(updatedTitle, result.Value.Title); + Assert.IsType(result.Value); + + _mockArtRepository.Verify( + r => r.Update(It.IsAny()), + Times.Once); + + _mockRepository.Verify( + r => r.SaveChangesAsync(It.IsAny()), + Times.Once); + } + + [Fact] + public async Task Handle_ArtNotFound_ReturnError() + { + // Arrange + const int artId = 1; + + var updateDto = new ArtUpdateDto + { + Id = artId, + Title = "Updated Art", + ImageId = 1, + }; + + _mockArtRepository + .Setup(r => r.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>>())) + .ReturnsAsync((ArtEntity)null!); + + var command = new UpdateArtCommand(updateDto); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + Assert.True(result.IsFailed); + Assert.Equal( + string.Format(ErrorMessages.EntityNotFound, artId), + result.Errors.First().Message); + + _mockArtRepository.Verify( + r => r.Update(It.IsAny()), + Times.Never); + + _mockRepository.Verify( + r => r.SaveChangesAsync(It.IsAny()), + Times.Never); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs new file mode 100644 index 00000000..dd2c9be4 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs @@ -0,0 +1,116 @@ +using System.Linq.Expressions; +using System.Transactions; +using AutoMapper; +using FluentAssertions; +using MockQueryable.Moq; +using Moq; +using Repositories.Interfaces; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Interfaces.Logging; +using Streetcode.BLL.Mapping.Media.Images; +using Streetcode.BLL.MediatR.Media.ArtSlide.Create; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Entities.Streetcode; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Streetcode; +using Xunit; +using ArtEntity = Streetcode.DAL.Entities.Media.Images.Art; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.Create; + +public class CreateArtSlideHandlerTests +{ + private readonly Mock _repoWrapperMock; + private readonly Mock _streetcodeRepoMock; + private readonly Mock _artRepoMock; + private readonly Mock _slideRepoMock; + private readonly Mock _slideItemRepoMock; + private readonly IMapper _mapper; + private readonly Mock _loggerMock; + private readonly CreateArtSlideHandler _handler; + + public CreateArtSlideHandlerTests() + { + var config = new MapperConfiguration(cfg => + { + cfg.AddProfile(); + }); + + _mapper = config.CreateMapper(); + + _repoWrapperMock = new Mock(); + _streetcodeRepoMock = new Mock(); + _artRepoMock = new Mock(); + _slideRepoMock = new Mock(); + _slideItemRepoMock = new Mock(); + _loggerMock = new Mock(); + + _repoWrapperMock.Setup(r => r.StreetcodeRepository).Returns(_streetcodeRepoMock.Object); + _repoWrapperMock.Setup(r => r.ArtRepository).Returns(_artRepoMock.Object); + _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); + _repoWrapperMock.Setup(r => r.ArtSlideItemRepository).Returns(_slideItemRepoMock.Object); + + _handler = new CreateArtSlideHandler(_mapper, _repoWrapperMock.Object); + } + + [Fact] + public async Task Handle_ShouldReturnSlideDto_WhenDataIsValid() + { + // Arrange + var requestDto = CreateRequestDto(); + var command = new CreateArtSlideCommand(requestDto); + + _streetcodeRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync(new StreetcodeContent { Id = requestDto.StreetcodeId }); + + var arts = new List { new() { Id = 1 } }.AsQueryable().BuildMock(); + _artRepoMock.Setup(r => r.GetAllAsync(It.IsAny>>(), null)) + .ReturnsAsync(arts.ToList()); + + _artRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync(new ArtEntity { Id = 1 }); + + // Настройка транзакции (обязательно для метода Handle) + _repoWrapperMock.Setup(r => r.BeginTransaction()) + .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); + + _repoWrapperMock.Setup(w => w.SaveChangesAsync(It.IsAny())).ReturnsAsync(1); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().NotBeNull(); + + _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.AtLeast(2)); + _slideRepoMock.Verify(r => r.Create(It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_ShouldReturnFail_WhenStreetcodeDoesNotExist() + { + // Arrange + var requestDto = CreateRequestDto(); + var command = new CreateArtSlideCommand(requestDto); + + _streetcodeRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync((StreetcodeContent)null!); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle(e => e.Message.Contains(string.Format(ErrorMessages.StreetcodeNotFound, requestDto.StreetcodeId))); + } + + private static CreateStreetcodeArtSlideDto CreateRequestDto() => new() + { + StreetcodeId = 1, + TemplateId = 1, + ArtSlideItems = new List { new() { ArtId = 1, Index = 0 } } + }; +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs new file mode 100644 index 00000000..53f32427 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs @@ -0,0 +1,76 @@ +using System.Linq.Expressions; +using System.Transactions; +using AutoMapper; +using FluentAssertions; +using Moq; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Mapping.Media.Images; +using Streetcode.BLL.MediatR.Media.ArtSlide.Create; +using Streetcode.BLL.MediatR.Media.ArtSlide.CreateAll; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Entities.Streetcode; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Streetcode; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.CreateAll; + +public class CreateAllArtSlidesHandlerTests +{ + private readonly Mock _repoWrapperMock; + private readonly Mock _streetcodeRepoMock; + private readonly Mock _slideRepoMock; + private readonly Mock _slideItemRepoMock; + private readonly IMapper _mapper; + private readonly CreateAllArtSlidesHandler _handler; + + public CreateAllArtSlidesHandlerTests() + { + var config = new MapperConfiguration(cfg => + { + cfg.AddProfile(); + }); + + _mapper = config.CreateMapper(); + + _repoWrapperMock = new Mock(); + _streetcodeRepoMock = new Mock(); + _slideRepoMock = new Mock(); + _slideItemRepoMock = new Mock(); + + _repoWrapperMock.Setup(r => r.StreetcodeRepository).Returns(_streetcodeRepoMock.Object); + _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); + _repoWrapperMock.Setup(r => r.ArtSlideItemRepository).Returns(_slideItemRepoMock.Object); + + _handler = new CreateAllArtSlidesHandler(_mapper, _repoWrapperMock.Object); + } + + [Fact] + public async Task Handle_ShouldReturnSlideDto_WhenDataIsValid() + { + // Arrange + var requestDto = new List + { + new() { StreetcodeId = 1, ArtSlideItems = new() { new() { ArtId = 1 } } } + }; + var command = new CreateAllArtSlidesCommand(requestDto); + + _streetcodeRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync(new StreetcodeContent { Id = 1 }); + + _repoWrapperMock.Setup(r => r.BeginTransaction()) + .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); + + _repoWrapperMock.Setup(w => w.SaveChangesAsync(It.IsAny())) + .ReturnsAsync(1); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + _slideRepoMock.Verify(r => r.Create(It.IsAny()), Times.Once); + _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.AtLeast(2)); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs new file mode 100644 index 00000000..00f657f9 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs @@ -0,0 +1,75 @@ +using System.Linq.Expressions; +using System.Transactions; +using FluentAssertions; +using Moq; +using Streetcode.BLL.MediatR.Media.ArtSlide.Delete; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.Delete; + +public class DeleteArtSlideHandlerTests +{ + private readonly Mock _repoWrapperMock; + private readonly Mock _slideRepoMock; + private readonly Mock _slideItemRepoMock; + private readonly DeleteArtSlideHandler _handler; + + public DeleteArtSlideHandlerTests() + { + _repoWrapperMock = new Mock(); + _slideRepoMock = new Mock(); + _slideItemRepoMock = new Mock(); + + _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); + _repoWrapperMock.Setup(r => r.ArtSlideItemRepository).Returns(_slideItemRepoMock.Object); + + _handler = new DeleteArtSlideHandler(_repoWrapperMock.Object); + } + + [Fact] + public async Task Handle_ShouldDeleteSlideAndItems_WhenSlideExists() + { + // Arrange + int slideId = 1; + var command = new DeleteArtSlideCommand(slideId); + var slide = new StreetcodeArtSlide { Id = slideId }; + + _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync(slide); + + _repoWrapperMock.Setup(r => r.BeginTransaction()) + .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + + _slideItemRepoMock.Verify(r => r.DeleteRange(It.IsAny>()), Times.Once); + _slideRepoMock.Verify(r => r.Delete(It.IsAny()), Times.Once); + _repoWrapperMock.Verify(r => r.SaveChangesAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_ShouldReturnFail_WhenSlideDoesNotExist() + { + // Arrange + int slideId = 99; + var command = new DeleteArtSlideCommand(slideId); + + _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync((StreetcodeArtSlide)null!); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle(e => e.Message.Contains(string.Format(ErrorMessages.SlideNotFound, slideId))); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandlerTests.cs new file mode 100644 index 00000000..8d7d7125 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandlerTests.cs @@ -0,0 +1,84 @@ +using System.Linq.Expressions; +using AutoMapper; +using FluentAssertions; +using Microsoft.EntityFrameworkCore.Query; +using MockQueryable.Moq; +using Moq; +using Streetcode.BLL.Mapping.Media.Images; +using Streetcode.BLL.MediatR.Media.ArtSlide.GetAllByStreetcodeId; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.GetAllByStreetcodeId; + +public class GetAllArtSlidesByStreetcodeIdHandlerTests +{ + private readonly Mock _repoWrapperMock; + private readonly Mock _slideRepoMock; + private readonly IMapper _mapper; + private readonly GetAllArtSlidesByStreetcodeIdHandler _handler; + + public GetAllArtSlidesByStreetcodeIdHandlerTests() + { + var config = new MapperConfiguration(cfg => { cfg.AddProfile(); }); + _mapper = config.CreateMapper(); + + _repoWrapperMock = new Mock(); + _slideRepoMock = new Mock(); + + _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); + _handler = new GetAllArtSlidesByStreetcodeIdHandler(_mapper, _repoWrapperMock.Object); + } + + [Fact] + public async Task Handle_ShouldReturnSlides_WhenStreetcodeIdIsValid() + { + // Arrange + int streetcodeId = 1; + var command = new GetAllArtSlidesByStreetcodeIdQuery(streetcodeId); + + var slides = new List + { + new() { Id = 1, StreetcodeId = streetcodeId } + }.AsQueryable().BuildMock(); + + // Используем корректный тип Func для include + _slideRepoMock.Setup(r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>>())) + .ReturnsAsync(slides.ToList()); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().NotBeNull(); + + // В Verify используем те же самые типы + _slideRepoMock.Verify( + r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>>()), + Times.Once); + } + + [Fact] + public async Task Handle_ShouldReturnFail_WhenSlidesAreNull() + { + // Arrange + _slideRepoMock.Setup(r => r.GetAllAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>>())) + .ReturnsAsync((List)null!); + + // Act + var result = await _handler.Handle(new GetAllArtSlidesByStreetcodeIdQuery(1), CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle(e => e.Message == "Slides not found"); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs new file mode 100644 index 00000000..d616f5d6 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs @@ -0,0 +1,95 @@ +using AutoMapper; +using FluentAssertions; +using Moq; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Mapping.Media.Images; +using Streetcode.BLL.MediatR.Media.ArtSlide.Update; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using System.Linq.Expressions; +using System.Transactions; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.Update; + +public class UpdateArtSlideHandlerTests +{ + private readonly Mock _repoWrapperMock; + private readonly Mock _slideRepoMock; + private readonly Mock _slideItemRepoMock; + private readonly IMapper _mapper; + private readonly UpdateArtSlideHandler _handler; + + public UpdateArtSlideHandlerTests() + { + var config = new MapperConfiguration(cfg => + { + cfg.AddProfile(); + }); + + _mapper = config.CreateMapper(); + + _repoWrapperMock = new Mock(); + _slideRepoMock = new Mock(); + _slideItemRepoMock = new Mock(); + + _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); + _repoWrapperMock.Setup(r => r.ArtSlideItemRepository).Returns(_slideItemRepoMock.Object); + + _handler = new UpdateArtSlideHandler(_mapper, _repoWrapperMock.Object); + } + + [Fact] + public async Task Handle_ShouldUpdateSlideAndRecreateItems_WhenDataIsValid() + { + // Arrange + var slideId = 1; + var requestDto = new UpdateArtSlideDto + { + Id = slideId, + Index = 5, + ArtSlideItems = new List { new() { ArtId = 10, Index = 0 } } + }; + var command = new UpdateArtSlideCommand(requestDto); + var existingSlide = new StreetcodeArtSlide { Id = slideId }; + + _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync(existingSlide); + + _repoWrapperMock.Setup(r => r.BeginTransaction()) + .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + + // Проверяем, что обновление состоялось + existingSlide.Index.Should().Be(5); + _slideRepoMock.Verify(r => r.Update(It.IsAny()), Times.Once); + + // Проверяем "пересборку" элементов + _slideItemRepoMock.Verify(r => r.DeleteRange(It.IsAny>()), Times.Once); + _slideItemRepoMock.Verify(r => r.Create(It.IsAny()), Times.Once); + _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_ShouldReturnFail_WhenSlideNotFound() + { + // Arrange + var requestDto = new UpdateArtSlideDto { Id = 99 }; + _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync((StreetcodeArtSlide)null!); + + // Act + var result = await _handler.Handle(new UpdateArtSlideCommand(requestDto), CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle(e => e.Message.Contains(string.Format(ErrorMessages.SlideNotFound, requestDto.Id))); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs new file mode 100644 index 00000000..18781c4b --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs @@ -0,0 +1,71 @@ +using AutoMapper; +using FluentAssertions; +using Moq; +using Streetcode.BLL.DTO.Media.ArtSlidesTemplates; +using Streetcode.BLL.Interfaces.Logging; +using Streetcode.BLL.Mapping.Media.Images; +using Streetcode.BLL.MediatR.Media.ArtSlideTeamplates.GetAll; +using Streetcode.BLL.Resources; +using Streetcode.DAL.Entities.Media.Images; +using Streetcode.DAL.Repositories.Interfaces.Base; +using Streetcode.DAL.Repositories.Interfaces.Media.Images; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlideTemplate.GetAll; + +public class GetAllArtSlideTemplatesHandlerTests +{ + private readonly Mock _repoWrapperMock; + private readonly Mock _templateRepoMock; + private readonly IMapper _mapper; + private readonly Mock _loggerMock; + private readonly GetAllArtSlideTemplatesHandler _handler; + + public GetAllArtSlideTemplatesHandlerTests() + { + var config = new MapperConfiguration(cfg => + { + cfg.AddProfile(); + }); + + _mapper = config.CreateMapper(); + + _repoWrapperMock = new Mock(); + _templateRepoMock = new Mock(); + _loggerMock = new Mock(); + + _repoWrapperMock.Setup(r => r.StreetcodeArtSlideTemplateRepository).Returns(_templateRepoMock.Object); + _handler = new GetAllArtSlideTemplatesHandler(_repoWrapperMock.Object, _mapper, _loggerMock.Object); + } + + [Fact] + public async Task Handle_ShouldReturnTemplates_WhenTemplatesExist() + { + // Arrange + var templates = new List { new() { Id = 1 } }; + _templateRepoMock.Setup(r => r.GetAllAsync(null, null)).ReturnsAsync(templates); + + // Act + var result = await _handler.Handle(new GetAllArtSlideTemplatesQuery(), CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().HaveCount(1); + _loggerMock.Verify(l => l.LogError(It.IsAny(), It.IsAny()), Times.Never); + } + + [Fact] + public async Task Handle_ShouldReturnFail_WhenTemplatesAreEmpty() + { + // Arrange + _templateRepoMock.Setup(r => r.GetAllAsync(null, null)).ReturnsAsync(new List()); + + // Act + var result = await _handler.Handle(new GetAllArtSlideTemplatesQuery(), CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle(e => e.Message == ErrorMessages.CannotFindAnyArtSlideTemplates); + _loggerMock.Verify(l => l.LogError(It.IsAny(), ErrorMessages.CannotFindAnyArtSlideTemplates), Times.Once); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/ArtCreateDtoValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/ArtCreateDtoValidatorTests.cs new file mode 100644 index 00000000..27ec1ad3 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/ArtCreateDtoValidatorTests.cs @@ -0,0 +1,41 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.Validators.Media.Art.Create; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.Art +{ + public class ArtCreateDtoValidatorTests + { + private readonly ArtCreateDtoValidator _validator; + + public ArtCreateDtoValidatorTests() + { + _validator = new ArtCreateDtoValidator(); + } + + [Fact] + public void Should_Have_Error_When_ImageId_Is_Zero() + { + var model = new ArtCreateDto { ImageId = 0, Title = "Valid Title" }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.ImageId); + } + + [Fact] + public void Should_Have_Error_When_Title_Exceeds_Max_Length() + { + var model = new ArtCreateDto { ImageId = 1, Title = new string('a', 151) }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.Title); + } + + [Fact] + public void Should_Not_Have_Error_When_Data_Is_Valid() + { + var model = new ArtCreateDto { ImageId = 1, Title = "Valid Title" }; + var result = _validator.TestValidate(model); + result.ShouldNotHaveAnyValidationErrors(); + } + } +} diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/ArtUpdateDtoValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/ArtUpdateDtoValidatorTests.cs new file mode 100644 index 00000000..3e041370 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/ArtUpdateDtoValidatorTests.cs @@ -0,0 +1,31 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.Validators.Media.Art; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.Art +{ + public class ArtUpdateDtoValidatorTests + { + private readonly ArtUpdateDtoValidator _validator = new(); + + [Fact] + public void DtoValidator_Should_Detect_Errors_When_Fields_Are_Invalid() + { + var model = new ArtUpdateDto { Id = 0, ImageId = 0, Title = new string('a', 151) }; + var result = _validator.TestValidate(model); + + result.ShouldHaveValidationErrorFor(x => x.Id); + result.ShouldHaveValidationErrorFor(x => x.ImageId); + result.ShouldHaveValidationErrorFor(x => x.Title); + } + + [Fact] + public void DtoValidator_Should_Pass_When_Data_Is_Valid() + { + var model = new ArtUpdateDto { Id = 1, ImageId = 1, Title = "Valid" }; + var result = _validator.TestValidate(model); + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/CreateArtCommandValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/CreateArtCommandValidatorTests.cs new file mode 100644 index 00000000..4c6c9b29 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/CreateArtCommandValidatorTests.cs @@ -0,0 +1,25 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.MediatR.Media.Art.Create; +using Streetcode.BLL.Validators.Media.Art; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.Art +{ + public class CreateArtCommandValidatorTests + { + private readonly CreateArtCommandValidator _validator; + + public CreateArtCommandValidatorTests() + { + _validator = new CreateArtCommandValidator(); + } + + [Fact] + public void Should_Have_Error_When_ArtDto_Is_Null() + { + var command = new CreateArtCommand(null!); + var result = _validator.TestValidate(command); + result.ShouldHaveValidationErrorFor(x => x.ArtDto); + } + } +} diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/DeleteArtCommandValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/DeleteArtCommandValidatorTests.cs new file mode 100644 index 00000000..eed0d769 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/DeleteArtCommandValidatorTests.cs @@ -0,0 +1,40 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.MediatR.Media.Art.Delete; +using Streetcode.BLL.Validators.Media.Art.Delete; +using Xunit; + +namespace Streetcode.XUnitTest.Validators.Media.Art.Delete +{ + public class DeleteArtCommandValidatorTests + { + private readonly DeleteArtCommandValidator _validator; + + public DeleteArtCommandValidatorTests() + { + _validator = new DeleteArtCommandValidator(); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + [InlineData(-999)] + public void Should_Have_Error_When_Id_Is_Less_Or_Equal_To_Zero(int invalidId) + { + var command = new DeleteArtCommand(invalidId); + + var result = _validator.TestValidate(command); + + result.ShouldHaveValidationErrorFor(x => x.Id); + } + + [Fact] + public void Should_Not_Have_Errors_When_Id_Is_Valid() + { + var command = new DeleteArtCommand(1); + + var result = _validator.TestValidate(command); + + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/UpdateArtCommandValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/UpdateArtCommandValidatorTests.cs new file mode 100644 index 00000000..70bea8d9 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/Art/UpdateArtCommandValidatorTests.cs @@ -0,0 +1,28 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.MediatR.Media.Art.Update; +using Streetcode.BLL.Validators.Media.Art; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.Art +{ + public class UpdateArtCommandValidatorTests + { + private readonly UpdateArtCommandValidator _validator = new(); + + [Fact] + public void CommandValidator_Should_Detect_Error_When_Dto_Is_Null() + { + var command = new UpdateArtCommand(null!); + var result = _validator.TestValidate(command); + result.ShouldHaveValidationErrorFor(x => x.ArtDto); + } + + [Fact] + public void CommandValidator_Should_Call_Nested_Validator_And_Fail_On_Invalid_Dto() + { + var command = new UpdateArtCommand(new() { Id = 0 }); + var result = _validator.TestValidate(command); + result.ShouldHaveValidationErrorFor(x => x.ArtDto.Id); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/ArtSlideItemDtoValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/ArtSlideItemDtoValidatorTests.cs new file mode 100644 index 00000000..c7f02e32 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/ArtSlideItemDtoValidatorTests.cs @@ -0,0 +1,40 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Validators.Media.ArtSlides; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlides +{ + public class ArtSlideItemDtoValidatorTests + { + private readonly ArtSlideItemDtoValidator _validator = new(); + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void Should_Have_Error_When_ArtId_Is_Not_Positive(int invalidId) + { + var model = new ArtSlideItemDto { ArtId = invalidId, Index = 0 }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.ArtId); + } + + [Theory] + [InlineData(-1)] + [InlineData(-100)] + public void Should_Have_Error_When_Index_Is_Negative(int invalidIndex) + { + var model = new ArtSlideItemDto { ArtId = 1, Index = invalidIndex }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.Index); + } + + [Fact] + public void Should_Not_Have_Errors_When_Data_Is_Valid() + { + var model = new ArtSlideItemDto { ArtId = 1, Index = 0 }; + var result = _validator.TestValidate(model); + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Create/CreateArtSlideCommandValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Create/CreateArtSlideCommandValidatorTests.cs new file mode 100644 index 00000000..7ddc7059 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Create/CreateArtSlideCommandValidatorTests.cs @@ -0,0 +1,55 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.MediatR.Media.ArtSlide.Create; +using Streetcode.BLL.Validators.Media.ArtSlides.Create; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlides +{ + public class CreateArtSlideCommandValidatorTests + { + private readonly CreateArtSlideCommandValidator _validator = new(); + + [Fact] + public void Should_Have_Error_When_Dto_Is_Null() + { + var command = new CreateArtSlideCommand(null!); + var result = _validator.TestValidate(command); + result.ShouldHaveValidationErrorFor(x => x.Dto); + } + + [Fact] + public void Should_Have_Error_When_Dto_Fields_Are_Invalid() + { + var invalidDto = new CreateStreetcodeArtSlideDto + { + Index = -5, + TemplateId = 1, + StreetcodeId = 1, + ArtSlideItems = new List() + }; + + var command = new CreateArtSlideCommand(invalidDto); + var result = _validator.TestValidate(command); + + result.ShouldHaveValidationErrorFor(x => x.Dto.Index); + } + + [Fact] + public void Should_Not_Have_Errors_When_Dto_Is_Valid() + { + var validDto = new CreateStreetcodeArtSlideDto + { + Index = 0, + TemplateId = 1, + StreetcodeId = 1, + ArtSlideItems = new List() + }; + + var command = new CreateArtSlideCommand(validDto); + var result = _validator.TestValidate(command); + + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Create/CreateStreetcodeArtSlideDtoValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Create/CreateStreetcodeArtSlideDtoValidatorTests.cs new file mode 100644 index 00000000..02ff2e79 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Create/CreateStreetcodeArtSlideDtoValidatorTests.cs @@ -0,0 +1,34 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Validators.Media.ArtSlides.Create; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlides +{ + public class CreateStreetcodeArtSlideDtoValidatorTests + { + private readonly CreateStreetcodeArtSlideDtoValidator _validator = new(); + + [Fact] + public void Should_Have_Error_When_Index_Is_Negative() + { + var model = new CreateStreetcodeArtSlideDto { Index = -1 }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.Index); + } + + [Fact] + public void Should_Not_Have_Errors_When_Data_Is_Valid() + { + var model = new CreateStreetcodeArtSlideDto + { + Index = 1, + TemplateId = 1, + StreetcodeId = 1, + ArtSlideItems = new List() + }; + var result = _validator.TestValidate(model); + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/CreateAll/CreateAllArtSlidesCommandValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/CreateAll/CreateAllArtSlidesCommandValidatorTests.cs new file mode 100644 index 00000000..1514ec53 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/CreateAll/CreateAllArtSlidesCommandValidatorTests.cs @@ -0,0 +1,39 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.MediatR.Media.ArtSlide.CreateAll; +using Streetcode.BLL.Validators.Media.ArtSlides; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlides +{ + public class CreateAllArtSlidesCommandValidatorTests + { + private readonly CreateAllArtSlidesCommandValidator _validator = new(); + + [Fact] + public void Should_Have_Error_When_ArtSlides_Is_Null_Or_Empty() + { + var commandNull = new CreateAllArtSlidesCommand(null!); + var commandEmpty = new CreateAllArtSlidesCommand(new List()); + + var resultNull = _validator.TestValidate(commandNull); + var resultEmpty = _validator.TestValidate(commandEmpty); + + resultNull.ShouldHaveValidationErrorFor(x => x.ArtSlides); + resultEmpty.ShouldHaveValidationErrorFor(x => x.ArtSlides); + } + + [Fact] + public void Should_Have_Error_When_List_Contains_Invalid_Item() + { + var command = new CreateAllArtSlidesCommand(new List + { + new() { Index = -1 } + }); + + var result = _validator.TestValidate(command); + + result.ShouldHaveValidationErrorFor("ArtSlides[0].Index"); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Delete/DeleteArtSlideCommandValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Delete/DeleteArtSlideCommandValidatorTests.cs new file mode 100644 index 00000000..4401406c --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Delete/DeleteArtSlideCommandValidatorTests.cs @@ -0,0 +1,33 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.MediatR.Media.ArtSlide.Delete; +using Streetcode.BLL.Validators.Media.ArtSlides.Delete; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlides.Delete +{ + public class DeleteArtSlideCommandValidatorTests + { + private readonly DeleteArtSlideCommandValidator _validator = new(); + + [Theory] + [InlineData(0)] + [InlineData(-1)] + [InlineData(-100)] + public void Should_Have_Error_When_Id_Is_Not_Positive(int invalidId) + { + var command = new DeleteArtSlideCommand(invalidId); + var result = _validator.TestValidate(command); + + result.ShouldHaveValidationErrorFor(x => x.Id); + } + + [Fact] + public void Should_Not_Have_Errors_When_Id_Is_Valid() + { + var command = new DeleteArtSlideCommand(1); + var result = _validator.TestValidate(command); + + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQueryValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQueryValidatorTests.cs new file mode 100644 index 00000000..c09a252a --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdQueryValidatorTests.cs @@ -0,0 +1,32 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.MediatR.Media.ArtSlide.GetAllByStreetcodeId; +using Streetcode.BLL.Validators.Media.ArtSlides.GetByStreetcodeId; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlides.GetByStreetcodeId +{ + public class GetAllArtSlidesByStreetcodeIdQueryValidatorTests + { + private readonly GetAllArtSlidesByStreetcodeIdQueryValidator _validator = new(); + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void Should_Have_Error_When_StreetcodeId_Is_Not_Positive(int invalidId) + { + var query = new GetAllArtSlidesByStreetcodeIdQuery(invalidId); + var result = _validator.TestValidate(query); + + result.ShouldHaveValidationErrorFor(x => x.StreetcodeId); + } + + [Fact] + public void Should_Not_Have_Errors_When_StreetcodeId_Is_Valid() + { + var query = new GetAllArtSlidesByStreetcodeIdQuery(1); + var result = _validator.TestValidate(query); + + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideCommandValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideCommandValidatorTests.cs new file mode 100644 index 00000000..437d80d6 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideCommandValidatorTests.cs @@ -0,0 +1,30 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.MediatR.Media.ArtSlide.Update; +using Streetcode.BLL.Validators.Media.ArtSlides; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlides.Update +{ + public class UpdateArtSlideCommandValidatorTests + { + private readonly UpdateArtSlideCommandValidator _validator = new(); + + [Fact] + public void Should_Have_Error_When_Dto_Is_Null() + { + var command = new UpdateArtSlideCommand(null!); + var result = _validator.TestValidate(command); + result.ShouldHaveValidationErrorFor(x => x.Dto); + } + + [Fact] + public void Should_Have_Error_When_Nested_Dto_Is_Invalid() + { + var command = new UpdateArtSlideCommand(new UpdateArtSlideDto { Id = 0 }); + var result = _validator.TestValidate(command); + + result.ShouldHaveValidationErrorFor(x => x.Dto.Id); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideDtoValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideDtoValidatorTests.cs new file mode 100644 index 00000000..d85e277d --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlides/Update/UpdateArtSlideDtoValidatorTests.cs @@ -0,0 +1,34 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.Validators.Media.ArtSlides.Update; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlides.Update +{ + public class UpdateArtSlideDtoValidatorTests + { + private readonly UpdateArtSlideDtoValidator _validator = new(); + + [Fact] + public void Should_Have_Error_When_Id_Is_Zero() + { + var model = new UpdateArtSlideDto { Id = 0 }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.Id); + } + + [Fact] + public void Should_Not_Have_Errors_When_Data_Is_Valid() + { + var model = new UpdateArtSlideDto + { + Id = 1, + Index = 1, + TemplateId = 1, + StreetcodeId = 1 + }; + var result = _validator.TestValidate(model); + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDtoValidatorTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDtoValidatorTests.cs new file mode 100644 index 00000000..a329797c --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Validators/Media/ArtSlidesTemplates/StreetcodeArtSlideTemplateDtoValidatorTests.cs @@ -0,0 +1,44 @@ +using FluentValidation.TestHelper; +using Streetcode.BLL.DTO.Media.ArtSlidesTemplates; +using Streetcode.BLL.Validators.Media.ArtSlidesTemplates; +using Xunit; + +namespace Streetcode.XUnitTest.BLL.Validators.Media.ArtSlidesTemplates +{ + public class StreetcodeArtSlideTemplateDtoValidatorTests + { + private readonly StreetcodeArtSlideTemplateDtoValidator _validator = new(); + + [Fact] + public void Should_Have_Error_When_Id_Is_Zero_Or_Negative() + { + var model = new StreetcodeArtSlideTemplateDto { Id = 0, Name = "Valid Name" }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.Id); + } + + [Fact] + public void Should_Have_Error_When_Name_Is_Empty() + { + var model = new StreetcodeArtSlideTemplateDto { Id = 1, Name = "" }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.Name); + } + + [Fact] + public void Should_Have_Error_When_Name_Exceeds_Max_Length() + { + var model = new StreetcodeArtSlideTemplateDto { Id = 1, Name = new string('a', 256) }; + var result = _validator.TestValidate(model); + result.ShouldHaveValidationErrorFor(x => x.Name); + } + + [Fact] + public void Should_Not_Have_Errors_When_Data_Is_Valid() + { + var model = new StreetcodeArtSlideTemplateDto { Id = 1, Name = "Valid Template Name" }; + var result = _validator.TestValidate(model); + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs new file mode 100644 index 00000000..1e66b662 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs @@ -0,0 +1,107 @@ +using System.Reflection; +using FluentAssertions; +using FluentResults; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Streetcode.BLL.DTO.Media.Art; +using Streetcode.BLL.MediatR.Media.Art.Create; +using Streetcode.BLL.MediatR.Media.Art.GetAll; +using Streetcode.BLL.MediatR.Media.Art.GetById; +using Streetcode.WebApi.Controllers; +using Streetcode.WebApi.Controllers.Media.Images; +using Xunit; + +namespace Streetcode.XUnitTest.WebApi.Controllers.Media.Images; + +public class ArtControllerTests +{ + private readonly Mock _mediatorMock; + private readonly ArtController _controller; + + public ArtControllerTests() + { + _mediatorMock = new Mock(); + _controller = new ArtController(); + + var type = typeof(BaseApiController); + + // ❗ 1. ИЩЕМ FIELD (реальный источник Mediator) + var field = + type.GetField("_mediator", BindingFlags.Instance | BindingFlags.NonPublic) + ?? type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) + .FirstOrDefault(f => f.FieldType == typeof(IMediator)); + + if (field == null) + throw new Exception("Cannot find IMediator field in BaseApiController"); + + field.SetValue(_controller, _mediatorMock.Object); + } + + [Fact] + public async Task GetAll_ShouldReturnOk_WhenResultIsSuccess() + { + var expected = Result.Ok>(new List()); + + _mediatorMock + .Setup(m => m.Send( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(expected); + + var result = await _controller.GetAll(); + + result.Should().BeOfType(); + + _mediatorMock.Verify( + m => m.Send(It.IsAny(), It.IsAny()), + Times.Once); + } + + [Fact] + public async Task GetById_ShouldReturnOk_WhenArtExists() + { + const int id = 1; + + var expected = Result.Ok(new ArtDTO + { + Id = id + }); + + _mediatorMock + .Setup(m => m.Send( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(expected); + + var result = await _controller.GetById(id); + + result.Should().BeOfType(); + + _mediatorMock.Verify( + m => m.Send(It.IsAny(), It.IsAny()), + Times.Once); + } + + [Fact] + public async Task Create_ShouldReturnOk_WhenDataIsValid() + { + var dto = new ArtCreateDto(); + + var expected = Result.Ok(new ArtDTO()); + + _mediatorMock + .Setup(m => m.Send( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(expected); + + var result = await _controller.Create(dto); + + result.Should().BeOfType(); + + _mediatorMock.Verify( + m => m.Send(It.IsAny(), It.IsAny()), + Times.Once); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtSlideControllerTests.cs b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtSlideControllerTests.cs new file mode 100644 index 00000000..5b077cc1 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtSlideControllerTests.cs @@ -0,0 +1,122 @@ +using System.Reflection; +using FluentAssertions; +using FluentResults; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Streetcode.BLL.DTO.Media.ArtSlides; +using Streetcode.BLL.MediatR.Media.ArtSlide.Create; +using Streetcode.BLL.MediatR.Media.ArtSlide.Delete; +using Streetcode.BLL.MediatR.Media.ArtSlide.GetAllByStreetcodeId; +using Streetcode.BLL.MediatR.Media.ArtSlide.Update; +using Streetcode.WebApi.Controllers; +using Streetcode.WebApi.Controllers.Media; +using Xunit; + +namespace Streetcode.XUnitTest.WebApi.Controllers.Media; + +public class ArtSlideControllerTests +{ + private readonly Mock _mediatorMock; + private readonly ArtSlideController _controller; + + public ArtSlideControllerTests() + { + _mediatorMock = new Mock(); + _controller = new ArtSlideController(); + + var type = typeof(BaseApiController); + + var field = + type.GetField("_mediator", BindingFlags.Instance | BindingFlags.NonPublic) + ?? type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) + .FirstOrDefault(f => f.FieldType == typeof(IMediator)); + + if (field == null) + { + throw new Exception("Mediator field not found in BaseApiController"); + } + + field.SetValue(_controller, _mediatorMock.Object); + } + + [Fact] + public async Task GetAllByStreetcodeId_ShouldReturnOk() + { + const int streetcodeId = 1; + + _mediatorMock + .Setup(m => m.Send( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Ok>( + new List())); + + var result = await _controller.GetAllByStreetcodeId(streetcodeId); + + result.Should().BeOfType(); + + _mediatorMock.Verify( + m => m.Send(It.IsAny(), It.IsAny()), + Times.Once); + } + + [Fact] + public async Task Create_ShouldCallMediator() + { + var dto = new CreateStreetcodeArtSlideDto + { + StreetcodeId = 1, + TemplateId = 1, + Index = 0 + }; + + _mediatorMock + .Setup(m => m.Send( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Ok(new StreetcodeArtSlideDto())); + + var result = await _controller.Create(dto); + + result.Should().BeOfType(); + } + + [Fact] + public async Task Update_ShouldCallMediator() + { + var dto = new UpdateArtSlideDto + { + Id = 1, + StreetcodeId = 1, + TemplateId = 1, + Index = 0 + }; + + _mediatorMock + .Setup(m => m.Send( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Ok(Unit.Value)); + + var result = await _controller.Update(dto); + + result.Should().BeOfType(); + } + + [Fact] + public async Task Delete_ShouldCallMediator() + { + const int id = 1; + + _mediatorMock + .Setup(m => m.Send( + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Ok()); + + var result = await _controller.Delete(id); + + result.Should().BeOfType(); + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtSlideTeamplatesControllerTests.cs b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtSlideTeamplatesControllerTests.cs new file mode 100644 index 00000000..74a8389e --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtSlideTeamplatesControllerTests.cs @@ -0,0 +1,64 @@ +using System.Reflection; +using FluentAssertions; +using FluentResults; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Streetcode.BLL.DTO.Media.ArtSlidesTemplates; +using Streetcode.BLL.MediatR.Media.ArtSlideTeamplates.GetAll; +using Streetcode.WebApi.Controllers; +using Streetcode.WebApi.Controllers.Media; +using Xunit; + +namespace Streetcode.XUnitTest.WebApi.Controllers.Media; + +public class ArtSlideTeamplatesControllerTests +{ + private readonly Mock _mediatorMock; + private readonly ArtSlideTeamplatesController _controller; + + public ArtSlideTeamplatesControllerTests() + { + _mediatorMock = new Mock(); + _controller = new ArtSlideTeamplatesController(); + + var type = typeof(BaseApiController); + + var field = + type.GetField("_mediator", BindingFlags.Instance | BindingFlags.NonPublic) + ?? type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) + .FirstOrDefault(f => f.FieldType == typeof(IMediator)); + + if (field == null) + { + throw new Exception("Mediator field not found in BaseApiController"); + } + + field.SetValue(_controller, _mediatorMock.Object); + } + + [Fact] + public async Task GetAll_ShouldReturnOk_WhenDataExists() + { + // Arrange + _mediatorMock + .Setup(m => m.Send( + It.IsAny(), + It.IsAny())) + .ReturnsAsync( + Result.Ok>( + new List())); + + // Act + var result = await _controller.GetAll(); + + // Assert + result.Should().BeOfType(); + + _mediatorMock.Verify( + m => m.Send( + It.IsAny(), + It.IsAny()), + Times.Once); + } +} \ No newline at end of file From b69f2b54c430d5f451564da9ee8fd289c5390dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Mon, 15 Jun 2026 15:53:59 +0300 Subject: [PATCH 2/9] fix sonar issues --- .../Media/Art/Create/CreateArtHandler.cs | 4 ++-- .../Media/Art/Delete/DeleteArtHandler.cs | 10 ++++++---- .../Media/Art/Update/UpdateArtHandler.cs | 10 ++++++---- .../ArtSlide/Create/CreateArtSlideHandler.cs | 18 +++++++++++------- .../CreateAll/CreateAllArtSlidesHandler.cs | 15 ++++++++------- .../ArtSlide/Delete/DeleteArtSlideHandler.cs | 9 +++++---- .../ArtSlide/Update/UpdateArtSlideHandler.cs | 11 ++++++----- .../Streetcode.DAL/Enums/ArtSlideTemplate.cs | 1 - .../Realizations/Base/RepositoryWrapper.cs | 4 +++- .../Controllers/Media/ArtSlideController.cs | 2 +- .../Controllers/Media/Images/ArtController.cs | 1 - .../Update/UpdateArtSlideHandlerTests.cs | 6 +++--- .../Media/Images/ArtControllerTests.cs | 5 +++-- 13 files changed, 54 insertions(+), 42 deletions(-) diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtHandler.cs index d28cee0d..5fff5ab2 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Create/CreateArtHandler.cs @@ -17,11 +17,11 @@ public CreateArtHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) _repositoryWrapper = repositoryWrapper; } - public async Task> Handle(CreateArtCommand request, CancellationToken ct) + public async Task> Handle(CreateArtCommand request, CancellationToken cancellationToken) { var art = _mapper.Map(request.ArtDto); await _repositoryWrapper.ArtRepository.CreateAsync(art); - await _repositoryWrapper.SaveChangesAsync(); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); return Result.Ok(_mapper.Map(art)); } diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs index f1da44e6..7de43a43 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs @@ -14,10 +14,12 @@ public DeleteArtHandler(IRepositoryWrapper repositoryWrapper) _repositoryWrapper = repositoryWrapper; } - public async Task> Handle(DeleteArtCommand request, CancellationToken ct) + public async Task> Handle(DeleteArtCommand request, CancellationToken cancellationToken) { - var art = await _repositoryWrapper.ArtRepository - .GetFirstOrDefaultAsync(a => a.Id == request.Id); + var art = await _repositoryWrapper.ArtRepository.GetFirstOrDefaultAsync( + predicate: a => a.Id == request.Id, + cancellationToken: cancellationToken + ); if (art == null) { @@ -26,7 +28,7 @@ public async Task> Handle(DeleteArtCommand request, CancellationTok _repositoryWrapper.ArtRepository.Delete(art); - await _repositoryWrapper.SaveChangesAsync(ct); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); return Result.Ok(Unit.Value); } diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs index 11c26c12..b9b75d69 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs @@ -18,10 +18,12 @@ public UpdateArtHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) _repositoryWrapper = repositoryWrapper; } - public async Task> Handle(UpdateArtCommand request, CancellationToken ct) + public async Task> Handle(UpdateArtCommand request, CancellationToken cancellationToken) { - var art = await _repositoryWrapper.ArtRepository - .GetFirstOrDefaultAsync(a => a.Id == request.ArtDto.Id); + var art = await _repositoryWrapper.ArtRepository.GetFirstOrDefaultAsync( + predicate: a => a.Id == request.ArtDto.Id, + cancellationToken: cancellationToken + ); if (art == null) { @@ -31,7 +33,7 @@ public async Task> Handle(UpdateArtCommand request, CancellationT _mapper.Map(request.ArtDto, art); _repositoryWrapper.ArtRepository.Update(art); - await _repositoryWrapper.SaveChangesAsync(ct); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); return Result.Ok(_mapper.Map(art)); } diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs index 18a8d285..4c6e5e12 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs @@ -20,10 +20,11 @@ public CreateArtSlideHandler(IMapper mapper, IRepositoryWrapper repositoryWrappe _repositoryWrapper = repositoryWrapper; } - public async Task> Handle(CreateArtSlideCommand request, CancellationToken ct) + public async Task> Handle(CreateArtSlideCommand request, CancellationToken cancellationToken) { - var streetcode = await _repositoryWrapper.StreetcodeRepository - .GetFirstOrDefaultAsync(s => s.Id == request.Dto.StreetcodeId); + var streetcode = await _repositoryWrapper.StreetcodeRepository.GetFirstOrDefaultAsync( + predicate: s => s.Id == request.Dto.StreetcodeId, + cancellationToken: cancellationToken); if (streetcode == null) { @@ -44,16 +45,19 @@ public async Task> Handle(CreateArtSlideCommand re { var newSlide = _mapper.Map(request.Dto); _repositoryWrapper.StreetcodeArtSlideRepository.Create(newSlide); - await _repositoryWrapper.SaveChangesAsync(ct); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); foreach (var artItem in request.Dto.ArtSlideItems) { - var artExists = await _repositoryWrapper.ArtRepository.GetFirstOrDefaultAsync(a => a.Id == artItem.ArtId); + var artExists = await _repositoryWrapper.ArtRepository.GetFirstOrDefaultAsync( + predicate: a => a.Id == artItem.ArtId, + cancellationToken: cancellationToken); + if (artExists == null) { return Result.Fail($"Art with id {artItem.ArtId} does not exist."); } - _repositoryWrapper.ArtSlideItemRepository.Create(new ArtSlideItem + await _repositoryWrapper.ArtSlideItemRepository.CreateAsync(new ArtSlideItem { SlideId = newSlide.Id, ArtId = artItem.ArtId, @@ -61,7 +65,7 @@ public async Task> Handle(CreateArtSlideCommand re }); } - await _repositoryWrapper.SaveChangesAsync(ct); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); transaction.Complete(); diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs index bb0880e0..e2c716b3 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs @@ -22,17 +22,18 @@ public CreateAllArtSlidesHandler(IMapper mapper, IRepositoryWrapper repositoryWr public async Task>> Handle( CreateAllArtSlidesCommand request, - CancellationToken ct) + CancellationToken cancellationToken) { - if (request.ArtSlides == null || !request.ArtSlides.Any()) + if (request.ArtSlides == null || request.ArtSlides.Count == 0) { return Result.Fail("ArtSlides list is empty"); } var streetcodeId = request.ArtSlides.First().StreetcodeId; - var streetcode = await _repositoryWrapper.StreetcodeRepository - .GetFirstOrDefaultAsync(s => s.Id == streetcodeId); + var streetcode = await _repositoryWrapper.StreetcodeRepository.GetFirstOrDefaultAsync( + predicate: s => s.Id == streetcodeId, + cancellationToken: cancellationToken); if (streetcode == null) { @@ -53,8 +54,8 @@ public async Task>> Handle( var newSlide = _mapper.Map(dto); - _repositoryWrapper.StreetcodeArtSlideRepository.Create(newSlide); - await _repositoryWrapper.SaveChangesAsync(ct); + await _repositoryWrapper.StreetcodeArtSlideRepository.CreateAsync(newSlide); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); foreach (var artItem in dto.ArtSlideItems) { @@ -67,7 +68,7 @@ public async Task>> Handle( }); } - await _repositoryWrapper.SaveChangesAsync(ct); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); resultList.Add(_mapper.Map(newSlide)); } diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs index b98874cf..08e2bc4f 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs @@ -14,10 +14,11 @@ public DeleteArtSlideHandler(IRepositoryWrapper repositoryWrapper) _repositoryWrapper = repositoryWrapper; } - public async Task> Handle(DeleteArtSlideCommand request, CancellationToken ct) + public async Task> Handle(DeleteArtSlideCommand request, CancellationToken cancellationToken) { - var slide = await _repositoryWrapper.StreetcodeArtSlideRepository - .GetFirstOrDefaultAsync(s => s.Id == request.Id); + var slide = await _repositoryWrapper.StreetcodeArtSlideRepository.GetFirstOrDefaultAsync( + predicate: s => s.Id == request.Id, + cancellationToken: cancellationToken); if (slide == null) { @@ -33,7 +34,7 @@ public async Task> Handle(DeleteArtSlideCommand request, Cancellati _repositoryWrapper.StreetcodeArtSlideRepository.Delete(slide); - await _repositoryWrapper.SaveChangesAsync(ct); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); transaction.Complete(); } diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs index 90751cd3..1e3f05fe 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs @@ -18,10 +18,11 @@ public UpdateArtSlideHandler(IMapper mapper, IRepositoryWrapper repositoryWrappe _repositoryWrapper = repositoryWrapper; } - public async Task> Handle(UpdateArtSlideCommand request, CancellationToken ct) + public async Task> Handle(UpdateArtSlideCommand request, CancellationToken cancellationToken) { - var slide = await _repositoryWrapper.StreetcodeArtSlideRepository - .GetFirstOrDefaultAsync(s => s.Id == request.Dto.Id); + var slide = await _repositoryWrapper.StreetcodeArtSlideRepository.GetFirstOrDefaultAsync( + predicate: s => s.Id == request.Dto.Id, + cancellationToken: cancellationToken); if (slide == null) { @@ -41,7 +42,7 @@ public async Task> Handle(UpdateArtSlideCommand request, Cancellati foreach (var item in request.Dto.ArtSlideItems) { - _repositoryWrapper.ArtSlideItemRepository.Create(new ArtSlideItem + await _repositoryWrapper.ArtSlideItemRepository.CreateAsync(new ArtSlideItem { SlideId = slide.Id, ArtId = item.ArtId, @@ -49,7 +50,7 @@ public async Task> Handle(UpdateArtSlideCommand request, Cancellati }); } - await _repositoryWrapper.SaveChangesAsync(ct); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); transaction.Complete(); } diff --git a/Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs b/Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs index 6cf0aca8..ffce3b85 100644 --- a/Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs +++ b/Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs @@ -16,6 +16,5 @@ public enum ArtSlideTemplate OneAndTwoAndThreeToFourAndFive = 11, OneAndTwoAndThreeToFourAndFiveAndSix = 12, OneAndTwoAndThreeAndFourAndFive = 13, - } } diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs index 043dd15e..ecd4938f 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; +using System.Transactions; using Microsoft.EntityFrameworkCore; using Repositories.Interfaces; using Streetcode.DAL.Persistence; @@ -32,10 +34,10 @@ using Streetcode.DAL.Repositories.Realizations.Toponyms; using Streetcode.DAL.Repositories.Realizations.Transactions; using Streetcode.DAL.Repositories.Realizations.Users; -using System.Transactions; namespace Streetcode.DAL.Repositories.Realizations.Base; +[ExcludeFromCodeCoverage] public class RepositoryWrapper : IRepositoryWrapper { private readonly StreetcodeDbContext _streetcodeDbContext; diff --git a/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideController.cs b/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideController.cs index 283bcb6e..2cc8f363 100644 --- a/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideController.cs +++ b/Streetcode/Streetcode.WebApi/Controllers/Media/ArtSlideController.cs @@ -32,7 +32,7 @@ await base.Mediator.Send(new CreateArtSlideCommand(dto), cancellationToken) } [HttpPost("CreateAll")] - //[AuthorizeRoles(UserRole.MainAdministrator)] + [AuthorizeRoles(UserRole.MainAdministrator)] public async Task CreateAll([FromBody] List dtos, CancellationToken cancellationToken = default) { return base.HandleResult( diff --git a/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs b/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs index a2d6c162..bdb7aa90 100644 --- a/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs +++ b/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs @@ -10,7 +10,6 @@ using Streetcode.DAL.Enums; using Streetcode.WebApi.Attributes; - namespace Streetcode.WebApi.Controllers.Media.Images; [ExcludeFromCodeCoverage] diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs index d616f5d6..fd6ef978 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs @@ -1,4 +1,6 @@ -using AutoMapper; +using System.Linq.Expressions; +using System.Transactions; +using AutoMapper; using FluentAssertions; using Moq; using Streetcode.BLL.DTO.Media.ArtSlides; @@ -8,8 +10,6 @@ using Streetcode.DAL.Entities.Media.Images; using Streetcode.DAL.Repositories.Interfaces.Base; using Streetcode.DAL.Repositories.Interfaces.Media.Images; -using System.Linq.Expressions; -using System.Transactions; using Xunit; namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.Update; diff --git a/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs index 1e66b662..f72d8b81 100644 --- a/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs @@ -26,14 +26,15 @@ public ArtControllerTests() var type = typeof(BaseApiController); - // ❗ 1. ИЩЕМ FIELD (реальный источник Mediator) var field = type.GetField("_mediator", BindingFlags.Instance | BindingFlags.NonPublic) ?? type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) .FirstOrDefault(f => f.FieldType == typeof(IMediator)); if (field == null) - throw new Exception("Cannot find IMediator field in BaseApiController"); + { + throw new Exception("Cannot find IMediator field in BaseApiController"); + } field.SetValue(_controller, _mediatorMock.Object); } From 11e1180f7c4a3b4efe559954245332e7a5e72cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Mon, 15 Jun 2026 17:44:19 +0300 Subject: [PATCH 3/9] fix appsetting - delete last "}" --- Streetcode/Streetcode.WebApi/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Streetcode/Streetcode.WebApi/appsettings.json b/Streetcode/Streetcode.WebApi/appsettings.json index 110eedd0..50c5e57a 100644 --- a/Streetcode/Streetcode.WebApi/appsettings.json +++ b/Streetcode/Streetcode.WebApi/appsettings.json @@ -80,4 +80,4 @@ "BaseUrl": "https://nominatim.openstreetmap.org/search" } } -} + From 2004c8599302e76a1a7ea80dd6d627b045f51d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Mon, 15 Jun 2026 18:16:57 +0300 Subject: [PATCH 4/9] Fix ErrorMessages.resx by taking version from dev --- .../Resources/ErrorMessages.resx | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx index 55be43c9..569de4b3 100644 --- a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx +++ b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx @@ -708,28 +708,7 @@ Found result matching null - - Cannot find any art slide templates - - - One or more specified arts were not found. - - - Slide with ID {0} was not found. - - - Entity is not found - - - Id must be greater than 0 - - - Art slide items are required. - - - Request is required. - - - {0} must not exceed {1} characters. + + News with id {0} was not found \ No newline at end of file From f7eeb321a2db9c89a85e66aa295e8a0047601fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Mon, 15 Jun 2026 18:45:24 +0300 Subject: [PATCH 5/9] fix sonar error --- .../Resources/ErrorMessages.Designer.cs | 21 +++++++++++----- .../Resources/ErrorMessages.resx | 24 +++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.Designer.cs b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.Designer.cs index add448b4..c2908469 100644 --- a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.Designer.cs +++ b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.Designer.cs @@ -97,7 +97,7 @@ public static string ArtSlideItemsIsRequired { } /// - /// Looks up a localized string similar to One or more specified arts were not found.. + /// Looks up a localized string similar to Arts not found. /// public static string ArtsNotFound { get { @@ -187,7 +187,7 @@ public static string CannotFindAnyArts { } /// - /// Looks up a localized string similar to Cannot find any art slide templates. + /// Looks up a localized string similar to Cannot find any artSlideTemplates. /// public static string CannotFindAnyArtSlideTemplates { get { @@ -682,7 +682,7 @@ public static string EndDateCannotBeEarlierThanStartDate { } /// - /// Looks up a localized string similar to Entity is not found. + /// Looks up a localized string similar to Entity not found . /// public static string EntityNotFound { get { @@ -1051,7 +1051,7 @@ public static string InvalidEnumValue { } /// - /// Looks up a localized string similar to Id must be greater than 0. + /// Looks up a localized string similar to Invalid Id. /// public static string InvalidId { get { @@ -1203,6 +1203,15 @@ public static string NewsIsRequired { } } + /// + /// Looks up a localized string similar to News with id {0} was not found. + /// + public static string NewsWithIdNotFound { + get { + return ResourceManager.GetString("NewsWithIdNotFound", resourceCulture); + } + } + /// /// Looks up a localized string similar to No existing streetcode with id: {0}. /// @@ -1420,7 +1429,7 @@ public static string RelatedWordAlreadyExists { } /// - /// Looks up a localized string similar to Request is required.. + /// Looks up a localized string similar to Request is required. /// public static string RequestIsRequired { get { @@ -1447,7 +1456,7 @@ public static string SearchQueryMustNotExceedCharacters { } /// - /// Looks up a localized string similar to Slide with ID {0} was not found.. + /// Looks up a localized string similar to Slide not found. /// public static string SlideNotFound { get { diff --git a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx index 569de4b3..9cfc6623 100644 --- a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx +++ b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx @@ -711,4 +711,28 @@ News with id {0} was not found + + Entity not found + + + Invalid Id + + + {0} must not exceed {1} characters. + + + Request is required + + + Art slide items are required. + + + Slide not found + + + Cannot find any artSlideTemplates + + + Arts not found + \ No newline at end of file From 615eaa48e5939cfa874128535980c5f747d2e52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Mon, 15 Jun 2026 19:07:17 +0300 Subject: [PATCH 6/9] fix tests --- .../ArtSlide/Create/CreateArtSlideHandler.cs | 3 +- .../Create/CreateArtSlideHandlerTests.cs | 156 +++++++++--------- .../CreateAllArtSlidesHandlerTests.cs | 11 +- .../Delete/DeleteArtSlideHandlerTests.cs | 6 +- .../Update/UpdateArtSlideHandlerTests.cs | 7 +- .../GetAllArtSlideTemplatesHandlerTests.cs | 7 +- 6 files changed, 101 insertions(+), 89 deletions(-) diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs index 4c6e5e12..39dd6948 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs @@ -2,7 +2,6 @@ using FluentResults; using MediatR; using Streetcode.BLL.DTO.Media.ArtSlides; -using Streetcode.BLL.MediatR.Media.ArtSlide.Create; using Streetcode.BLL.Resources; using Streetcode.DAL.Entities.Media.Images; using Streetcode.DAL.Repositories.Interfaces.Base; @@ -44,7 +43,7 @@ public async Task> Handle(CreateArtSlideCommand re using (var transaction = _repositoryWrapper.BeginTransaction()) { var newSlide = _mapper.Map(request.Dto); - _repositoryWrapper.StreetcodeArtSlideRepository.Create(newSlide); + await _repositoryWrapper.StreetcodeArtSlideRepository.CreateAsync(newSlide); await _repositoryWrapper.SaveChangesAsync(cancellationToken); foreach (var artItem in request.Dto.ArtSlideItems) diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs index dd2c9be4..93561a60 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs @@ -1,12 +1,12 @@ -using System.Linq.Expressions; -using System.Transactions; -using AutoMapper; +using AutoMapper; using FluentAssertions; using MockQueryable.Moq; using Moq; using Repositories.Interfaces; using Streetcode.BLL.DTO.Media.ArtSlides; using Streetcode.BLL.Interfaces.Logging; +using System.Linq.Expressions; +using System.Transactions; using Streetcode.BLL.Mapping.Media.Images; using Streetcode.BLL.MediatR.Media.ArtSlide.Create; using Streetcode.BLL.Resources; @@ -18,99 +18,101 @@ using Xunit; using ArtEntity = Streetcode.DAL.Entities.Media.Images.Art; -namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.Create; - -public class CreateArtSlideHandlerTests +namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.Create { - private readonly Mock _repoWrapperMock; - private readonly Mock _streetcodeRepoMock; - private readonly Mock _artRepoMock; - private readonly Mock _slideRepoMock; - private readonly Mock _slideItemRepoMock; - private readonly IMapper _mapper; - private readonly Mock _loggerMock; - private readonly CreateArtSlideHandler _handler; - - public CreateArtSlideHandlerTests() + public class CreateArtSlideHandlerTests { - var config = new MapperConfiguration(cfg => + private readonly Mock _repoWrapperMock; + private readonly Mock _streetcodeRepoMock; + private readonly Mock _artRepoMock; + private readonly Mock _slideRepoMock; + private readonly Mock _slideItemRepoMock; + private readonly IMapper _mapper; + private readonly CreateArtSlideHandler _handler; + + public CreateArtSlideHandlerTests() { - cfg.AddProfile(); - }); + var config = new MapperConfiguration(cfg => + { + cfg.AddProfile(); + }); - _mapper = config.CreateMapper(); + _mapper = config.CreateMapper(); - _repoWrapperMock = new Mock(); - _streetcodeRepoMock = new Mock(); - _artRepoMock = new Mock(); - _slideRepoMock = new Mock(); - _slideItemRepoMock = new Mock(); - _loggerMock = new Mock(); + _repoWrapperMock = new Mock(); + _streetcodeRepoMock = new Mock(); + _artRepoMock = new Mock(); + _slideRepoMock = new Mock(); + _slideItemRepoMock = new Mock(); - _repoWrapperMock.Setup(r => r.StreetcodeRepository).Returns(_streetcodeRepoMock.Object); - _repoWrapperMock.Setup(r => r.ArtRepository).Returns(_artRepoMock.Object); - _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); - _repoWrapperMock.Setup(r => r.ArtSlideItemRepository).Returns(_slideItemRepoMock.Object); + _repoWrapperMock.Setup(r => r.StreetcodeRepository).Returns(_streetcodeRepoMock.Object); + _repoWrapperMock.Setup(r => r.ArtRepository).Returns(_artRepoMock.Object); + _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); + _repoWrapperMock.Setup(r => r.ArtSlideItemRepository).Returns(_slideItemRepoMock.Object); - _handler = new CreateArtSlideHandler(_mapper, _repoWrapperMock.Object); - } + _handler = new CreateArtSlideHandler(_mapper, _repoWrapperMock.Object); + } - [Fact] - public async Task Handle_ShouldReturnSlideDto_WhenDataIsValid() - { - // Arrange - var requestDto = CreateRequestDto(); - var command = new CreateArtSlideCommand(requestDto); + [Fact] + public async Task Handle_ShouldReturnSlideDto_WhenDataIsValid() + { + // Arrange + var requestDto = CreateRequestDto(); + var command = new CreateArtSlideCommand(requestDto); - _streetcodeRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) - .ReturnsAsync(new StreetcodeContent { Id = requestDto.StreetcodeId }); + _streetcodeRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync(new StreetcodeContent { Id = requestDto.StreetcodeId }); - var arts = new List { new() { Id = 1 } }.AsQueryable().BuildMock(); - _artRepoMock.Setup(r => r.GetAllAsync(It.IsAny>>(), null)) - .ReturnsAsync(arts.ToList()); + var arts = new List { new() { Id = 1 } }.AsQueryable().BuildMock(); + _artRepoMock.Setup(r => r.GetAllAsync(It.IsAny>>(), null)) + .ReturnsAsync(arts.ToList()); - _artRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) - .ReturnsAsync(new ArtEntity { Id = 1 }); + _artRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync(new ArtEntity { Id = 1 }); - // Настройка транзакции (обязательно для метода Handle) - _repoWrapperMock.Setup(r => r.BeginTransaction()) - .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); + _repoWrapperMock.Setup(r => r.BeginTransaction()) + .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); - _repoWrapperMock.Setup(w => w.SaveChangesAsync(It.IsAny())).ReturnsAsync(1); + _repoWrapperMock.Setup(w => w.SaveChangesAsync(It.IsAny())).ReturnsAsync(1); - // Act - var result = await _handler.Handle(command, CancellationToken.None); + // Act + var result = await _handler.Handle(command, CancellationToken.None); - // Assert - result.IsSuccess.Should().BeTrue(); - result.Value.Should().NotBeNull(); + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().NotBeNull(); - _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.AtLeast(2)); - _slideRepoMock.Verify(r => r.Create(It.IsAny()), Times.Once); - } + _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.AtLeast(2)); - [Fact] - public async Task Handle_ShouldReturnFail_WhenStreetcodeDoesNotExist() - { - // Arrange - var requestDto = CreateRequestDto(); - var command = new CreateArtSlideCommand(requestDto); + _slideRepoMock.Verify(r => r.CreateAsync(It.IsAny()), Times.Once); + _slideItemRepoMock.Verify(r => r.CreateAsync(It.IsAny()), Times.Once); + } - _streetcodeRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) - .ReturnsAsync((StreetcodeContent)null!); + [Fact] + public async Task Handle_ShouldReturnFail_WhenStreetcodeDoesNotExist() + { + // Arrange + var requestDto = CreateRequestDto(); + var command = new CreateArtSlideCommand(requestDto); - // Act - var result = await _handler.Handle(command, CancellationToken.None); + _streetcodeRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync((StreetcodeContent)null!); - // Assert - result.IsFailed.Should().BeTrue(); - result.Errors.Should().ContainSingle(e => e.Message.Contains(string.Format(ErrorMessages.StreetcodeNotFound, requestDto.StreetcodeId))); - } + // Act + var result = await _handler.Handle(command, CancellationToken.None); - private static CreateStreetcodeArtSlideDto CreateRequestDto() => new() - { - StreetcodeId = 1, - TemplateId = 1, - ArtSlideItems = new List { new() { ArtId = 1, Index = 0 } } - }; + // Assert + result.IsFailed.Should().BeTrue(); + + string expectedMessage = ErrorMessages.StreetcodeNotFound ?? "Streetcode with id {0} not found."; + result.Errors.Should().ContainSingle(e => e.Message.Contains(string.Format(expectedMessage, requestDto.StreetcodeId))); + } + + private static CreateStreetcodeArtSlideDto CreateRequestDto() => new() + { + StreetcodeId = 1, + TemplateId = 1, + ArtSlideItems = new List { new() { ArtId = 1, Index = 0 } } + }; + } } \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs index 53f32427..52fd6a03 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs @@ -51,9 +51,9 @@ public async Task Handle_ShouldReturnSlideDto_WhenDataIsValid() { // Arrange var requestDto = new List - { - new() { StreetcodeId = 1, ArtSlideItems = new() { new() { ArtId = 1 } } } - }; + { + new() { StreetcodeId = 1, ArtSlideItems = new() { new() { ArtId = 1, Index = 0 } } } + }; var command = new CreateAllArtSlidesCommand(requestDto); _streetcodeRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) @@ -70,7 +70,10 @@ public async Task Handle_ShouldReturnSlideDto_WhenDataIsValid() // Assert result.IsSuccess.Should().BeTrue(); - _slideRepoMock.Verify(r => r.Create(It.IsAny()), Times.Once); + + _slideRepoMock.Verify(r => r.CreateAsync(It.IsAny()), Times.Once); + _slideItemRepoMock.Verify(r => r.Create(It.IsAny()), Times.Once); + _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.AtLeast(2)); } } \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs index 00f657f9..608d85ad 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs @@ -70,6 +70,10 @@ public async Task Handle_ShouldReturnFail_WhenSlideDoesNotExist() // Assert result.IsFailed.Should().BeTrue(); - result.Errors.Should().ContainSingle(e => e.Message.Contains(string.Format(ErrorMessages.SlideNotFound, slideId))); + + string errorMessageTemplate = ErrorMessages.SlideNotFound ?? "Slide with ID {0} was not found."; + string expectedMessage = string.Format(errorMessageTemplate, slideId); + + result.Errors.Should().ContainSingle(e => e.Message == expectedMessage); } } \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs index fd6ef978..e90a596f 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs @@ -58,6 +58,9 @@ public async Task Handle_ShouldUpdateSlideAndRecreateItems_WhenDataIsValid() _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) .ReturnsAsync(existingSlide); + _slideItemRepoMock.Setup(r => r.GetAllAsync(It.IsAny>>(), null)) + .ReturnsAsync(new List()); + _repoWrapperMock.Setup(r => r.BeginTransaction()) .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); @@ -67,13 +70,11 @@ public async Task Handle_ShouldUpdateSlideAndRecreateItems_WhenDataIsValid() // Assert result.IsSuccess.Should().BeTrue(); - // Проверяем, что обновление состоялось existingSlide.Index.Should().Be(5); _slideRepoMock.Verify(r => r.Update(It.IsAny()), Times.Once); - // Проверяем "пересборку" элементов _slideItemRepoMock.Verify(r => r.DeleteRange(It.IsAny>()), Times.Once); - _slideItemRepoMock.Verify(r => r.Create(It.IsAny()), Times.Once); + _slideItemRepoMock.Verify(r => r.CreateAsync(It.IsAny()), Times.Once); _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.Once); } diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs index 18781c4b..31fe4e22 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs @@ -65,7 +65,10 @@ public async Task Handle_ShouldReturnFail_WhenTemplatesAreEmpty() // Assert result.IsFailed.Should().BeTrue(); - result.Errors.Should().ContainSingle(e => e.Message == ErrorMessages.CannotFindAnyArtSlideTemplates); - _loggerMock.Verify(l => l.LogError(It.IsAny(), ErrorMessages.CannotFindAnyArtSlideTemplates), Times.Once); + string expectedMessage = ErrorMessages.CannotFindAnyArtSlideTemplates ?? "Cannot find any art slide templates."; + + result.Errors.Should().ContainSingle(e => e.Message == expectedMessage); + + _loggerMock.Verify(l => l.LogError(It.IsAny(), expectedMessage), Times.Once); } } \ No newline at end of file From a9886117090c3bc5b107bd26110caa063a5c0f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Mon, 15 Jun 2026 19:26:41 +0300 Subject: [PATCH 7/9] fix sonar issues --- .../CreateAll/CreateAllArtSlidesHandler.cs | 4 +- .../GetAllArtSlidesByStreetcodeIdHandler.cs | 2 +- .../ArtSlide/Update/UpdateArtSlideHandler.cs | 4 +- .../Realizations/Base/RepositoryBase.cs | 3 +- .../Create/CreateArtSlideHandlerTests.cs | 7 +- .../CreateAllArtSlidesHandlerTests.cs | 2 +- .../Update/UpdateArtSlideHandlerTests.cs | 139 ++++++++---------- .../Media/Images/ArtControllerTests.cs | 2 +- 8 files changed, 75 insertions(+), 88 deletions(-) diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs index e2c716b3..e6a24600 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs @@ -29,7 +29,7 @@ public async Task>> Handle( return Result.Fail("ArtSlides list is empty"); } - var streetcodeId = request.ArtSlides.First().StreetcodeId; + var streetcodeId = request.ArtSlides[0].StreetcodeId; var streetcode = await _repositoryWrapper.StreetcodeRepository.GetFirstOrDefaultAsync( predicate: s => s.Id == streetcodeId, @@ -59,7 +59,7 @@ public async Task>> Handle( foreach (var artItem in dto.ArtSlideItems) { - _repositoryWrapper.ArtSlideItemRepository.Create( + await _repositoryWrapper.ArtSlideItemRepository.CreateAsync( new ArtSlideItem { SlideId = newSlide.Id, diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandler.cs index e54a9c3c..e37c22ac 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/GetByStreetcodeId/GetAllArtSlidesByStreetcodeIdHandler.cs @@ -18,7 +18,7 @@ public GetAllArtSlidesByStreetcodeIdHandler(IMapper mapper, IRepositoryWrapper r _repositoryWrapper = repositoryWrapper; } - public async Task>> Handle(GetAllArtSlidesByStreetcodeIdQuery request, CancellationToken ct) + public async Task>> Handle(GetAllArtSlidesByStreetcodeIdQuery request, CancellationToken cancellationToken) { var slides = await _repositoryWrapper.StreetcodeArtSlideRepository.GetAllAsync( predicate: s => s.StreetcodeId == request.StreetcodeId, diff --git a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs index 1e3f05fe..8f774d61 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs @@ -9,12 +9,10 @@ namespace Streetcode.BLL.MediatR.Media.ArtSlide.Update { public class UpdateArtSlideHandler : IRequestHandler> { - private readonly IMapper _mapper; private readonly IRepositoryWrapper _repositoryWrapper; - public UpdateArtSlideHandler(IMapper mapper, IRepositoryWrapper repositoryWrapper) + public UpdateArtSlideHandler( IRepositoryWrapper repositoryWrapper) { - _mapper = mapper; _repositoryWrapper = repositoryWrapper; } diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs index ad4b75a8..1ad6142e 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs @@ -1,14 +1,15 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Query; -using MimeKit; using Streetcode.DAL.Persistence; using Streetcode.DAL.Repositories.Interfaces.Base; using Streetcode.DAL.Specifications.Base; namespace Streetcode.DAL.Repositories.Realizations.Base; +[ExcludeFromCodeCoverage] public abstract class RepositoryBase : IRepositoryBase where T : class { diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs index 93561a60..d6b5df15 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs @@ -1,12 +1,11 @@ -using AutoMapper; +using System.Linq.Expressions; +using System.Transactions; +using AutoMapper; using FluentAssertions; using MockQueryable.Moq; using Moq; using Repositories.Interfaces; using Streetcode.BLL.DTO.Media.ArtSlides; -using Streetcode.BLL.Interfaces.Logging; -using System.Linq.Expressions; -using System.Transactions; using Streetcode.BLL.Mapping.Media.Images; using Streetcode.BLL.MediatR.Media.ArtSlide.Create; using Streetcode.BLL.Resources; diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs index 52fd6a03..aba7bf18 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs @@ -72,7 +72,7 @@ public async Task Handle_ShouldReturnSlideDto_WhenDataIsValid() result.IsSuccess.Should().BeTrue(); _slideRepoMock.Verify(r => r.CreateAsync(It.IsAny()), Times.Once); - _slideItemRepoMock.Verify(r => r.Create(It.IsAny()), Times.Once); + _slideItemRepoMock.Verify(r => r.CreateAsync(It.IsAny()), Times.Once); _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.AtLeast(2)); } diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs index e90a596f..6fabe81e 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs @@ -1,10 +1,8 @@ using System.Linq.Expressions; using System.Transactions; -using AutoMapper; using FluentAssertions; using Moq; using Streetcode.BLL.DTO.Media.ArtSlides; -using Streetcode.BLL.Mapping.Media.Images; using Streetcode.BLL.MediatR.Media.ArtSlide.Update; using Streetcode.BLL.Resources; using Streetcode.DAL.Entities.Media.Images; @@ -12,85 +10,76 @@ using Streetcode.DAL.Repositories.Interfaces.Media.Images; using Xunit; -namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.Update; - -public class UpdateArtSlideHandlerTests +namespace Streetcode.XUnitTest.BLL.MediatR.Media.ArtSlide.Update { - private readonly Mock _repoWrapperMock; - private readonly Mock _slideRepoMock; - private readonly Mock _slideItemRepoMock; - private readonly IMapper _mapper; - private readonly UpdateArtSlideHandler _handler; - - public UpdateArtSlideHandlerTests() + public class UpdateArtSlideHandlerTests { - var config = new MapperConfiguration(cfg => - { - cfg.AddProfile(); - }); - - _mapper = config.CreateMapper(); - - _repoWrapperMock = new Mock(); - _slideRepoMock = new Mock(); - _slideItemRepoMock = new Mock(); + private readonly Mock _repoWrapperMock; + private readonly Mock _slideRepoMock; + private readonly Mock _slideItemRepoMock; + private readonly UpdateArtSlideHandler _handler; - _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); - _repoWrapperMock.Setup(r => r.ArtSlideItemRepository).Returns(_slideItemRepoMock.Object); - - _handler = new UpdateArtSlideHandler(_mapper, _repoWrapperMock.Object); - } - - [Fact] - public async Task Handle_ShouldUpdateSlideAndRecreateItems_WhenDataIsValid() - { - // Arrange - var slideId = 1; - var requestDto = new UpdateArtSlideDto + public UpdateArtSlideHandlerTests() { - Id = slideId, - Index = 5, - ArtSlideItems = new List { new() { ArtId = 10, Index = 0 } } - }; - var command = new UpdateArtSlideCommand(requestDto); - var existingSlide = new StreetcodeArtSlide { Id = slideId }; - - _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) - .ReturnsAsync(existingSlide); + _repoWrapperMock = new Mock(); + _slideRepoMock = new Mock(); + _slideItemRepoMock = new Mock(); - _slideItemRepoMock.Setup(r => r.GetAllAsync(It.IsAny>>(), null)) - .ReturnsAsync(new List()); + _repoWrapperMock.Setup(r => r.StreetcodeArtSlideRepository).Returns(_slideRepoMock.Object); + _repoWrapperMock.Setup(r => r.ArtSlideItemRepository).Returns(_slideItemRepoMock.Object); - _repoWrapperMock.Setup(r => r.BeginTransaction()) - .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); + _handler = new UpdateArtSlideHandler(_repoWrapperMock.Object); + } - // Act - var result = await _handler.Handle(command, CancellationToken.None); - - // Assert - result.IsSuccess.Should().BeTrue(); - - existingSlide.Index.Should().Be(5); - _slideRepoMock.Verify(r => r.Update(It.IsAny()), Times.Once); - - _slideItemRepoMock.Verify(r => r.DeleteRange(It.IsAny>()), Times.Once); - _slideItemRepoMock.Verify(r => r.CreateAsync(It.IsAny()), Times.Once); - _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.Once); - } - - [Fact] - public async Task Handle_ShouldReturnFail_WhenSlideNotFound() - { - // Arrange - var requestDto = new UpdateArtSlideDto { Id = 99 }; - _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) - .ReturnsAsync((StreetcodeArtSlide)null!); - - // Act - var result = await _handler.Handle(new UpdateArtSlideCommand(requestDto), CancellationToken.None); - - // Assert - result.IsFailed.Should().BeTrue(); - result.Errors.Should().ContainSingle(e => e.Message.Contains(string.Format(ErrorMessages.SlideNotFound, requestDto.Id))); + [Fact] + public async Task Handle_ShouldUpdateSlideAndRecreateItems_WhenDataIsValid() + { + // Arrange + var slideId = 1; + var requestDto = new UpdateArtSlideDto + { + Id = slideId, + Index = 5, + ArtSlideItems = new List { new() { ArtId = 10, Index = 0 } } + }; + var command = new UpdateArtSlideCommand(requestDto); + var existingSlide = new StreetcodeArtSlide { Id = slideId }; + + _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync(existingSlide); + + _slideItemRepoMock.Setup(r => r.GetAllAsync(It.IsAny>>(), null)) + .ReturnsAsync(new List()); + + _repoWrapperMock.Setup(r => r.BeginTransaction()) + .Returns(new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)); + + // Act + var result = await _handler.Handle(command, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + existingSlide.Index.Should().Be(5); + _slideRepoMock.Verify(r => r.Update(It.IsAny()), Times.Once); + _slideItemRepoMock.Verify(r => r.DeleteRange(It.IsAny>()), Times.Once); + _slideItemRepoMock.Verify(r => r.CreateAsync(It.IsAny()), Times.Once); + _repoWrapperMock.Verify(w => w.SaveChangesAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_ShouldReturnFail_WhenSlideNotFound() + { + // Arrange + var requestDto = new UpdateArtSlideDto { Id = 99 }; + _slideRepoMock.Setup(r => r.GetFirstOrDefaultAsync(It.IsAny>>(), null)) + .ReturnsAsync((StreetcodeArtSlide)null!); + + // Act + var result = await _handler.Handle(new UpdateArtSlideCommand(requestDto), CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle(e => e.Message.Contains(string.Format(ErrorMessages.SlideNotFound, requestDto.Id))); + } } } \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs index f72d8b81..0485b1b6 100644 --- a/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs @@ -33,7 +33,7 @@ public ArtControllerTests() if (field == null) { - throw new Exception("Cannot find IMediator field in BaseApiController"); + throw new Exception("Cannot find IMediator field in BaseApiController"); } field.SetValue(_controller, _mediatorMock.Object); From 710eba7bfa326441d4e53f3c220a9a009888554e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Mon, 15 Jun 2026 19:34:40 +0300 Subject: [PATCH 8/9] fix --- .../Repositories/Realizations/Base/RepositoryBase.cs | 1 - .../Repositories/Realizations/Base/RepositoryWrapper.cs | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs index 1ad6142e..6b06e68c 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs @@ -9,7 +9,6 @@ namespace Streetcode.DAL.Repositories.Realizations.Base; -[ExcludeFromCodeCoverage] public abstract class RepositoryBase : IRepositoryBase where T : class { diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs index ecd4938f..4dd24e5b 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs @@ -37,7 +37,6 @@ namespace Streetcode.DAL.Repositories.Realizations.Base; -[ExcludeFromCodeCoverage] public class RepositoryWrapper : IRepositoryWrapper { private readonly StreetcodeDbContext _streetcodeDbContext; @@ -116,7 +115,7 @@ public class RepositoryWrapper : IRepositoryWrapper private IStreetcodeArtSlideRepository? _streetcodeArtSlideRepository; private IStreetcodeArtSlideTemplateRepository? _streetcodeArtSlideTemplateRepository; - private IArtSlideItemRepository _artSlideItemRepository; + private IArtSlideItemRepository? _artSlideItemRepository; public RepositoryWrapper(StreetcodeDbContext streetcodeDbContext) { @@ -629,5 +628,4 @@ public IArtSlideItemRepository ArtSlideItemRepository return _artSlideItemRepository; } } - } From d3ea8efb7cd4ecb6fd42370a5ac648519c8494ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=BD=D0=BD=D0=B0=20=D0=A8=D0=BF=D0=BE=D0=BD=D1=8C?= =?UTF-8?q?=D0=BA=D0=B0?= Date: Thu, 18 Jun 2026 18:09:13 +0300 Subject: [PATCH 9/9] fix --- .../BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs | 2 +- .../BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs index 1e3bdd4f..9df0aea6 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Delete/DeleteArtHandlerTests.cs @@ -86,7 +86,7 @@ public async Task Handle_ArtNotFound_ReturnError() Assert.True(result.IsFailed); Assert.Equal( string.Format(ErrorMessages.EntityNotFound, artId), - result.Errors.First().Message); + result.Errors[0].Message); _mockArtRepository.Verify( r => r.Delete(It.IsAny()), diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs index 3dfd500d..38c6ca59 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/Art/Update/UpdateArtHandlerTests.cs @@ -116,7 +116,7 @@ public async Task Handle_ArtNotFound_ReturnError() Assert.True(result.IsFailed); Assert.Equal( string.Format(ErrorMessages.EntityNotFound, artId), - result.Errors.First().Message); + result.Errors[0].Message); _mockArtRepository.Verify( r => r.Update(It.IsAny()),