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/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..5fff5ab2 --- /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 cancellationToken) + { + var art = _mapper.Map(request.ArtDto); + await _repositoryWrapper.ArtRepository.CreateAsync(art); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + + 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..7de43a43 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Delete/DeleteArtHandler.cs @@ -0,0 +1,36 @@ +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 cancellationToken) + { + var art = await _repositoryWrapper.ArtRepository.GetFirstOrDefaultAsync( + predicate: a => a.Id == request.Id, + cancellationToken: cancellationToken + ); + + if (art == null) + { + return Result.Fail(string.Format(ErrorMessages.EntityNotFound, request.Id)); + } + + _repositoryWrapper.ArtRepository.Delete(art); + + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + + 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..b9b75d69 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/Art/Update/UpdateArtHandler.cs @@ -0,0 +1,41 @@ +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 cancellationToken) + { + var art = await _repositoryWrapper.ArtRepository.GetFirstOrDefaultAsync( + predicate: a => a.Id == request.ArtDto.Id, + cancellationToken: cancellationToken + ); + + 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(cancellationToken); + + 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..39dd6948 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandler.cs @@ -0,0 +1,75 @@ +using AutoMapper; +using FluentResults; +using MediatR; +using Streetcode.BLL.DTO.Media.ArtSlides; +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 cancellationToken) + { + var streetcode = await _repositoryWrapper.StreetcodeRepository.GetFirstOrDefaultAsync( + predicate: s => s.Id == request.Dto.StreetcodeId, + cancellationToken: cancellationToken); + + 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); + await _repositoryWrapper.StreetcodeArtSlideRepository.CreateAsync(newSlide); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + + foreach (var artItem in request.Dto.ArtSlideItems) + { + 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."); + } + await _repositoryWrapper.ArtSlideItemRepository.CreateAsync(new ArtSlideItem + { + SlideId = newSlide.Id, + ArtId = artItem.ArtId, + Index = artItem.Index + }); + } + + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + + 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..e6a24600 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandler.cs @@ -0,0 +1,82 @@ +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 cancellationToken) + { + if (request.ArtSlides == null || request.ArtSlides.Count == 0) + { + return Result.Fail("ArtSlides list is empty"); + } + + var streetcodeId = request.ArtSlides[0].StreetcodeId; + + var streetcode = await _repositoryWrapper.StreetcodeRepository.GetFirstOrDefaultAsync( + predicate: s => s.Id == streetcodeId, + cancellationToken: cancellationToken); + + 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); + + await _repositoryWrapper.StreetcodeArtSlideRepository.CreateAsync(newSlide); + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + + foreach (var artItem in dto.ArtSlideItems) + { + await _repositoryWrapper.ArtSlideItemRepository.CreateAsync( + new ArtSlideItem + { + SlideId = newSlide.Id, + ArtId = artItem.ArtId, + Index = artItem.Index + }); + } + + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + + 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..08e2bc4f --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandler.cs @@ -0,0 +1,45 @@ +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 cancellationToken) + { + var slide = await _repositoryWrapper.StreetcodeArtSlideRepository.GetFirstOrDefaultAsync( + predicate: s => s.Id == request.Id, + cancellationToken: cancellationToken); + + 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(cancellationToken); + + 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..e37c22ac --- /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 cancellationToken) + { + 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..8f774d61 --- /dev/null +++ b/Streetcode/Streetcode.BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandler.cs @@ -0,0 +1,58 @@ +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 IRepositoryWrapper _repositoryWrapper; + + public UpdateArtSlideHandler( IRepositoryWrapper repositoryWrapper) + { + _repositoryWrapper = repositoryWrapper; + } + + public async Task> Handle(UpdateArtSlideCommand request, CancellationToken cancellationToken) + { + var slide = await _repositoryWrapper.StreetcodeArtSlideRepository.GetFirstOrDefaultAsync( + predicate: s => s.Id == request.Dto.Id, + cancellationToken: cancellationToken); + + 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) + { + await _repositoryWrapper.ArtSlideItemRepository.CreateAsync(new ArtSlideItem + { + SlideId = slide.Id, + ArtId = item.ArtId, + Index = item.Index + }); + } + + await _repositoryWrapper.SaveChangesAsync(cancellationToken); + 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/MediatR/Newss/Update/UpdateNewsHandler.cs b/Streetcode/Streetcode.BLL/MediatR/Newss/Update/UpdateNewsHandler.cs index 36b6fa83..84745662 100644 --- a/Streetcode/Streetcode.BLL/MediatR/Newss/Update/UpdateNewsHandler.cs +++ b/Streetcode/Streetcode.BLL/MediatR/Newss/Update/UpdateNewsHandler.cs @@ -5,10 +5,9 @@ using Streetcode.BLL.Interfaces.BlobStorage; using Streetcode.BLL.Interfaces.Logging; using Streetcode.BLL.Resources; +using Streetcode.DAL.Entities.News; using Streetcode.DAL.Repositories.Interfaces.Base; -using NewsEntity = Streetcode.DAL.Entities.News.News; - namespace Streetcode.BLL.MediatR.Newss.Update { public class UpdateNewsHandler : IRequestHandler> @@ -17,12 +16,7 @@ public class UpdateNewsHandler : IRequestHandler> Handle(UpdateNewsCommand request, CancellationToken cancellationToken) { - var updateNews = request.news; - - if (updateNews is null) + var news = _mapper.Map(request.news); + if (news is null) { string errorMsg = ErrorMessages.CannotConvertNullToNews; _logger.LogError(request, errorMsg); return Result.Fail(new Error(errorMsg)); } - var newsEntity = await _repositoryWrapper.NewsRepository.GetFirstOrDefaultAsync( - predicate: n => n.Id == updateNews.Id, - cancellationToken: cancellationToken); + var response = _mapper.Map(news); - if (newsEntity is null) + if (news.Image is not null) { - string errorMsg = $"News with id {updateNews.Id} was not found"; - _logger.LogError(request, errorMsg); - return Result.Fail(new Error(errorMsg)); + response.Image?.Base64 = _blobSevice.FindFileInStorageAsBase64(response.Image.BlobName!); } + else + { + var img = await _repositoryWrapper.ImageRepository.GetFirstOrDefaultAsync( + predicate: x => x.Id == response.ImageId, + cancellationToken: cancellationToken); - newsEntity.Title = updateNews.Title; - newsEntity.Text = updateNews.Text; - newsEntity.URL = updateNews.URL; - newsEntity.CreationDate = updateNews.CreationDate; - newsEntity.ImageId = updateNews.ImageId; - - _repositoryWrapper.NewsRepository.Update(newsEntity); + if (img != null) + { + _repositoryWrapper.ImageRepository.Delete(img); + } + } + _repositoryWrapper.NewsRepository.Update(news); var resultIsSuccess = await _repositoryWrapper.SaveChangesAsync(cancellationToken) > 0; - if (!resultIsSuccess) + if (resultIsSuccess) + { + return Result.Ok(response); + } + else { string errorMsg = ErrorMessages.FailedToUpdateNews; _logger.LogError(request, errorMsg); return Result.Fail(new Error(errorMsg)); } - - var response = _mapper.Map(newsEntity); - - if (response.Image is not null) - { - response.Image.Base64 = _blobSevice.FindFileInStorageAsBase64(response.Image.BlobName!); - } - - return Result.Ok(response); } } -} \ 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 40dc8ca1..ea3e76e7 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 Arts 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 artSlideTemplates. + /// + public static string CannotFindAnyArtSlideTemplates { + get { + return ResourceManager.GetString("CannotFindAnyArtSlideTemplates", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot find any audios. /// @@ -663,6 +690,15 @@ public static string EndDateCannotBeEarlierThanStartDate { } } + /// + /// Looks up a localized string similar to Entity not found . + /// + public static string EntityNotFound { + get { + return ResourceManager.GetString("EntityNotFound", resourceCulture); + } + } + /// /// Looks up a localized string similar to Extension is required.. /// @@ -1032,6 +1068,15 @@ public static string InvalidEnumValue { } } + /// + /// Looks up a localized string similar to Invalid Id. + /// + public static string InvalidId { + get { + return ResourceManager.GetString("InvalidId", resourceCulture); + } + } + /// /// Looks up a localized string similar to The provided logo type is invalid.. /// @@ -1131,6 +1176,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.. /// @@ -1419,6 +1473,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.. /// @@ -1437,6 +1500,15 @@ public static string SearchQueryMustNotExceedCharacters { } } + /// + /// Looks up a localized string similar to Slide 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 aa544f72..b894648a 100644 --- a/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx +++ b/Streetcode/Streetcode.BLL/Resources/ErrorMessages.resx @@ -711,15 +711,36 @@ 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 + + Password must be at least {0} characters long. New password cannot be the same as the current one. - - {0} is required. - Invalid email format. 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..ffce3b85 --- /dev/null +++ b/Streetcode/Streetcode.DAL/Enums/ArtSlideTemplate.cs @@ -0,0 +1,20 @@ +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/RepositoryBase.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs index ad4b75a8..6b06e68c 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryBase.cs @@ -1,8 +1,8 @@ +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; diff --git a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs index 7d866a8d..4dd24e5b 100644 --- a/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs +++ b/Streetcode/Streetcode.DAL/Repositories/Realizations/Base/RepositoryWrapper.cs @@ -1,4 +1,6 @@ +using System.Diagnostics.CodeAnalysis; using System.Transactions; +using Microsoft.EntityFrameworkCore; using Repositories.Interfaces; using Streetcode.DAL.Persistence; using Streetcode.DAL.Repositories.Interfaces.AdditionalContent; @@ -20,6 +22,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; @@ -110,6 +113,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 +593,39 @@ 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..2cc8f363 --- /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..bdb7aa90 100644 --- a/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs +++ b/Streetcode/Streetcode.WebApi/Controllers/Media/Images/ArtController.cs @@ -1,8 +1,14 @@ 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 +38,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/Controllers/News/NewsController.cs b/Streetcode/Streetcode.WebApi/Controllers/News/NewsController.cs index 18283962..634d93fc 100644 --- a/Streetcode/Streetcode.WebApi/Controllers/News/NewsController.cs +++ b/Streetcode/Streetcode.WebApi/Controllers/News/NewsController.cs @@ -58,7 +58,7 @@ await base.Mediator.Send(new GetNewsAndLinksByUrlQuery(url), cancellationToken) } [HttpPost] - [AuthorizeRoles(UserRole.Administrator, UserRole.MainAdministrator)] + [AuthorizeRoles(UserRole.Administrator)] public async Task Create([FromBody] NewsDTO newsDto, CancellationToken cancellationToken = default) { return base.HandleResult( @@ -67,7 +67,7 @@ await base.Mediator.Send(new CreateNewsCommand(newsDto), cancellationToken) } [HttpPut("{id:int}")] - [AuthorizeRoles(UserRole.Administrator, UserRole.MainAdministrator)] + [AuthorizeRoles(UserRole.Administrator)] public async Task Update([FromRoute] int id, [FromBody] NewsDTO newsDto, CancellationToken cancellationToken = default) { newsDto.Id = id; @@ -77,7 +77,7 @@ await base.Mediator.Send(new UpdateNewsCommand(newsDto), cancellationToken) } [HttpDelete("{id:int}")] - [AuthorizeRoles(UserRole.Administrator, UserRole.MainAdministrator)] + [AuthorizeRoles(UserRole.Administrator)] public async Task Delete([FromRoute] int id, CancellationToken cancellationToken = default) { return base.HandleResult( 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 be4616ef..f82c842f 100644 --- a/Streetcode/Streetcode.WebApi/Program.cs +++ b/Streetcode/Streetcode.WebApi/Program.cs @@ -59,7 +59,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(); app.UseHttpsRedirection(); diff --git a/Streetcode/Streetcode.WebApi/appsettings.json b/Streetcode/Streetcode.WebApi/appsettings.json index 5fb66444..d79167f0 100644 --- a/Streetcode/Streetcode.WebApi/appsettings.json +++ b/Streetcode/Streetcode.WebApi/appsettings.json @@ -81,3 +81,4 @@ }, "UseAzureBlobStorage": false } + 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..9df0aea6 --- /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[0].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..38c6ca59 --- /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[0].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..d6b5df15 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Create/CreateArtSlideHandlerTests.cs @@ -0,0 +1,117 @@ +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.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 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(); + + _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 }); + + _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.CreateAsync(It.IsAny()), Times.Once); + _slideItemRepoMock.Verify(r => r.CreateAsync(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(); + + 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 new file mode 100644 index 00000000..aba7bf18 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/CreateAll/CreateAllArtSlidesHandlerTests.cs @@ -0,0 +1,79 @@ +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, Index = 0 } } } + }; + 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.CreateAsync(It.IsAny()), Times.Once); + _slideItemRepoMock.Verify(r => r.CreateAsync(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..608d85ad --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Delete/DeleteArtSlideHandlerTests.cs @@ -0,0 +1,79 @@ +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(); + + 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/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..6fabe81e --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlide/Update/UpdateArtSlideHandlerTests.cs @@ -0,0 +1,85 @@ +using System.Linq.Expressions; +using System.Transactions; +using FluentAssertions; +using Moq; +using Streetcode.BLL.DTO.Media.ArtSlides; +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 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 UpdateArtSlideHandler _handler; + + public UpdateArtSlideHandlerTests() + { + _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(_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); + + _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/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs new file mode 100644 index 00000000..31fe4e22 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/Media/ArtSlideTeamplates/GetAllArtSlideTemplatesHandlerTests.cs @@ -0,0 +1,74 @@ +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(); + 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 diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/News/Update/UpdateNewsHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/News/Update/UpdateNewsHandlerTests.cs index 03e7ca47..06b09364 100644 --- a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/News/Update/UpdateNewsHandlerTests.cs +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/News/Update/UpdateNewsHandlerTests.cs @@ -7,10 +7,10 @@ using Streetcode.BLL.Interfaces.Logging; using Streetcode.BLL.Mapping.Newss; using Streetcode.BLL.MediatR.Newss.Update; -using Streetcode.BLL.Resources; using Streetcode.DAL.Entities.Media.Images; using Streetcode.DAL.Repositories.Interfaces.Base; using Xunit; +using Streetcode.BLL.Resources; using NewsEntity = Streetcode.DAL.Entities.News.News; @@ -37,8 +37,8 @@ public UpdateNewsHandlerTests() _blobServiceMock = new Mock(); _loggerMock = new Mock(); - _repositoryWrapperMock - .Setup(r => r.NewsRepository.Update(It.IsAny())); + _repositoryWrapperMock.Setup(r => r.NewsRepository.Update(It.IsAny())); + _repositoryWrapperMock.Setup(r => r.ImageRepository.Delete(It.IsAny())); _handler = new UpdateNewsHandler( _repositoryWrapperMock.Object, @@ -48,7 +48,7 @@ public UpdateNewsHandlerTests() } [Fact] - public async Task Handle_ShouldReturnFail_WhenNewsDtoIsNull() + public async Task Handle_ShouldReturnFail_WhenMapperReturnsNull() { var request = new UpdateNewsCommand(null!); @@ -56,212 +56,89 @@ public async Task Handle_ShouldReturnFail_WhenNewsDtoIsNull() Assert.True(result.IsFailed); Assert.Equal(ErrorMessages.CannotConvertNullToNews, result.Errors[0].Message); - - _loggerMock.Verify( - l => l.LogError(request, ErrorMessages.CannotConvertNullToNews), - Times.Once); - - _repositoryWrapperMock.Verify( - r => r.NewsRepository.Update(It.IsAny()), - Times.Never); + _loggerMock.Verify(l => l.LogError(request, ErrorMessages.CannotConvertNullToNews), Times.Once); } [Fact] - public async Task Handle_ShouldReturnFail_WhenNewsDoesNotExist() + public async Task Handle_ShouldReturnOkAndFetchBase64_WhenImageIsNotNull() { var newsDto = new NewsDTO { Id = 1, - Title = "Updated title", - Text = "Updated text", - URL = "updated-url", - ImageId = 99, - CreationDate = DateTime.UtcNow + Image = new ImageDTO { BlobName = "test.jpg" } }; - var request = new UpdateNewsCommand(newsDto); - _repositoryWrapperMock - .Setup(r => r.NewsRepository.GetFirstOrDefaultAsync( - It.IsAny>>(), - null, - It.IsAny())) - .ReturnsAsync((NewsEntity)null!); + _blobServiceMock.Setup(b => b.FindFileInStorageAsBase64("test.jpg")) + .Returns("base64-string"); - var result = await _handler.Handle(request, CancellationToken.None); + _repositoryWrapperMock.Setup(r => r.SaveChangesAsync()).ReturnsAsync(1); - Assert.True(result.IsFailed); - Assert.Equal("News with id 1 was not found", result.Errors[0].Message); + var result = await _handler.Handle(request, CancellationToken.None); - _repositoryWrapperMock.Verify( - r => r.NewsRepository.Update(It.IsAny()), - Times.Never); + Assert.True(result.IsSuccess); + Assert.Equal("base64-string", result.Value.Image!.Base64); + _repositoryWrapperMock.Verify(r => r.NewsRepository.Update(It.Is(n => n.Id == 1)), Times.Once); + _blobServiceMock.Verify(b => b.FindFileInStorageAsBase64("test.jpg"), Times.Once); } [Fact] - public async Task Handle_ShouldReturnOkAndFetchBase64_WhenImageIsNotNull() + public async Task Handle_ShouldReturnOkAndDeleteOldImage_WhenImageIsNull_AndOldImageExists() { - var newsDto = new NewsDTO - { - Id = 1, - Title = "Updated title", - Text = "Updated text", - URL = "updated-url", - ImageId = 99, - CreationDate = DateTime.UtcNow - }; - - var existingNews = new NewsEntity - { - Id = 1, - Title = "Old title", - Text = "Old text", - URL = "old-url", - ImageId = 1, - Image = new Image - { - Id = 99, - BlobName = "test.jpg" - } - }; - + var newsDto = new NewsDTO { Id = 1, Image = null, ImageId = 99 }; var request = new UpdateNewsCommand(newsDto); - SetupExistingNews(existingNews); + var oldImageEntity = new Image { Id = 99 }; - _blobServiceMock - .Setup(b => b.FindFileInStorageAsBase64("test.jpg")) - .Returns("base64-string"); + _repositoryWrapperMock.Setup(r => r.ImageRepository.GetFirstOrDefaultAsync( + It.IsAny>>(), null)) + .ReturnsAsync(oldImageEntity); - _repositoryWrapperMock - .Setup(r => r.SaveChangesAsync(It.IsAny())) - .ReturnsAsync(1); + _repositoryWrapperMock.Setup(r => r.SaveChangesAsync()).ReturnsAsync(1); var result = await _handler.Handle(request, CancellationToken.None); Assert.True(result.IsSuccess); - Assert.Equal("base64-string", result.Value.Image!.Base64); - - _repositoryWrapperMock.Verify( - r => r.NewsRepository.Update(It.Is(n => - n.Id == 1 && - n.Title == "Updated title" && - n.Text == "Updated text" && - n.URL == "updated-url" && - n.ImageId == 99)), - Times.Once); - - _blobServiceMock.Verify( - b => b.FindFileInStorageAsBase64("test.jpg"), - Times.Once); + _repositoryWrapperMock.Verify(r => r.ImageRepository.Delete(oldImageEntity), Times.Once); + _repositoryWrapperMock.Verify(r => r.NewsRepository.Update(It.Is(n => n.Id == 1)), Times.Once); } [Fact] - public async Task Handle_ShouldReturnOkAndNotDeleteImage_WhenImageIsNull() + public async Task Handle_ShouldReturnOkAndNotDelete_WhenImageIsNull_AndOldImageDoesNotExist() { - var newsDto = new NewsDTO - { - Id = 1, - Title = "Updated title", - Text = "Updated text", - URL = "updated-url", - Image = null, - ImageId = 99, - CreationDate = DateTime.UtcNow - }; - - var existingNews = new NewsEntity - { - Id = 1, - Title = "Old title", - Text = "Old text", - URL = "old-url", - ImageId = 1, - Image = null - }; - + var newsDto = new NewsDTO { Id = 1, Image = null, ImageId = 99 }; var request = new UpdateNewsCommand(newsDto); - SetupExistingNews(existingNews); + _repositoryWrapperMock.Setup(r => r.ImageRepository.GetFirstOrDefaultAsync( + It.IsAny>>(), null)) + .ReturnsAsync((Image)null!); - _repositoryWrapperMock - .Setup(r => r.SaveChangesAsync(It.IsAny())) - .ReturnsAsync(1); + _repositoryWrapperMock.Setup(r => r.SaveChangesAsync()).ReturnsAsync(1); var result = await _handler.Handle(request, CancellationToken.None); Assert.True(result.IsSuccess); - - _repositoryWrapperMock.Verify( - r => r.ImageRepository.Delete(It.IsAny()), - Times.Never); - - _repositoryWrapperMock.Verify( - r => r.NewsRepository.Update(It.Is(n => - n.Id == 1 && - n.Title == "Updated title" && - n.Text == "Updated text" && - n.URL == "updated-url" && - n.ImageId == 99)), - Times.Once); + _repositoryWrapperMock.Verify(r => r.ImageRepository.Delete(It.IsAny()), Times.Never); + _repositoryWrapperMock.Verify(r => r.NewsRepository.Update(It.Is(n => n.Id == 1)), Times.Once); } [Fact] public async Task Handle_ShouldReturnFail_WhenSaveChangesReturnsZero() { - var newsDto = new NewsDTO - { - Id = 1, - Title = "Updated title", - Text = "Updated text", - URL = "updated-url", - Image = null, - ImageId = 99, - CreationDate = DateTime.UtcNow - }; - - var existingNews = new NewsEntity - { - Id = 1, - Title = "Old title", - Text = "Old text", - URL = "old-url", - ImageId = 1, - Image = null - }; - + var newsDto = new NewsDTO { Id = 1, Image = null }; var request = new UpdateNewsCommand(newsDto); - SetupExistingNews(existingNews); + _repositoryWrapperMock.Setup(r => r.ImageRepository.GetFirstOrDefaultAsync( + It.IsAny>>(), null)) + .ReturnsAsync((Image)null!); - _repositoryWrapperMock - .Setup(r => r.SaveChangesAsync(It.IsAny())) - .ReturnsAsync(0); + _repositoryWrapperMock.Setup(r => r.SaveChangesAsync()).ReturnsAsync(0); var result = await _handler.Handle(request, CancellationToken.None); Assert.True(result.IsFailed); Assert.Equal(ErrorMessages.FailedToUpdateNews, result.Errors[0].Message); - - _loggerMock.Verify( - l => l.LogError(request, ErrorMessages.FailedToUpdateNews), - Times.Once); - - _repositoryWrapperMock.Verify( - r => r.NewsRepository.Update(It.Is(n => - n.Id == 1 && - n.ImageId == 99)), - Times.Once); - } - - private void SetupExistingNews(NewsEntity existingNews) - { - _repositoryWrapperMock - .Setup(r => r.NewsRepository.GetFirstOrDefaultAsync( - It.IsAny>>(), - null, - It.IsAny())) - .ReturnsAsync(existingNews); + _loggerMock.Verify(l => l.LogError(request, ErrorMessages.FailedToUpdateNews), 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..0485b1b6 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/WebApi/Controllers/Media/Images/ArtControllerTests.cs @@ -0,0 +1,108 @@ +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); + + 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