diff --git a/ImageBase.WebApp.UnitTests/CatalogServiceTests.cs b/ImageBase.WebApp.UnitTests/CatalogServiceTests.cs index fe83f0d..49153a5 100644 --- a/ImageBase.WebApp.UnitTests/CatalogServiceTests.cs +++ b/ImageBase.WebApp.UnitTests/CatalogServiceTests.cs @@ -36,7 +36,7 @@ public CatalogServiceTests() } [Fact] - public async Task GetCatalogAsyncReturnsCatalogDto() + public async Task GetCatalogAsyncCallsMethOfRepositoryAndReturnsServiceResponse_IfCatalogWithUserExists() { var catalog = new Catalog() { @@ -45,39 +45,50 @@ public async Task GetCatalogAsyncReturnsCatalogDto() UserId = "some user" }; _catalogRepositoryMock.Setup(r => r.GetAsync(It.IsAny())).Returns(Task.FromResult(catalog)); + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(true)); - ServiceResponse catalogDto = await _service.GetCatalogAsync(1); + ServiceResponse serviceResponse = await _service.GetCatalogAsync(1); - Assert.NotNull(catalogDto); - Assert.IsAssignableFrom(catalogDto); - Assert.Equal(catalogDto.Data.Id, catalog.Id); - Assert.Equal(catalogDto.Data.Name, catalog.Name); - Assert.Equal(catalogDto.Data.UserId, catalog.UserId); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); + Assert.Equal(serviceResponse.Data.Id, catalog.Id); + Assert.Equal(serviceResponse.Data.Name, catalog.Name); + Assert.Equal(serviceResponse.Data.UserId, catalog.UserId); } + [Fact] - public async Task CreateCatalogAsyncCallsMethodOfRepository_And_SavesChanges() - { - await _service.CreateCatalogAsync(new CatalogDto()); + public async Task GetCatalogAsyncReturnsUnsuccesfullServiceResponse_IfCatalogWithUserDoesntExist() + { + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(false)); - _catalogRepositoryMock.Verify(r => r.Add(It.IsAny()), Times.Once); - _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Once); + ServiceResponse serviceResponse = await _service.GetCatalogAsync(1); + + _catalogRepositoryMock.Verify(r => r.GetAsync(It.IsAny()), Times.Never); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); + Assert.False(serviceResponse.Success); + Assert.NotNull(serviceResponse.Message); } [Fact] - public async Task CreateCatalogAsyncReturnsServiceResponse_IfCatalogWithNameExists() + public async Task CreateCatalogAsyncReturnsUnsuccesfullServiceResponse_IfCatalogWithNameExists() { CatalogDto catalogDto = new CatalogDto(); _catalogRepositoryMock.Setup(r => r.HasChildWithNameAsync(It.IsAny())) .Returns(Task.FromResult(true)); ServiceResponse serviceResponse = await _service.CreateCatalogAsync(catalogDto); - + + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); Assert.False(serviceResponse.Success); Assert.NotNull(serviceResponse.Message); - } + } [Fact] - public async Task CreateCatalogAsyncReturnsServiceResponse_IfCatalogWithNameDoesntExsist() + public async Task CreateCatalogAsyncCallsMethOfRepositoryAndReturnsServiceResponse_IfCatalogWithNameDoesntExsist() { CatalogDto catalogDto = new CatalogDto(); _catalogRepositoryMock.Setup(r => r.HasChildWithNameAsync(It.IsAny())) @@ -85,32 +96,53 @@ public async Task CreateCatalogAsyncReturnsServiceResponse_IfCatalogWithNameDoes ServiceResponse serviceResponse = await _service.CreateCatalogAsync(catalogDto); + _catalogRepositoryMock.Verify(r => r.Add(It.IsAny()), Times.Once); + _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Once); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); + Assert.True(serviceResponse.Success); + Assert.Equal(catalogDto, serviceResponse.Data); + } + + [Fact] + public async Task DeleteCatalogAsyncCallsMethOfRepositoryAndReturnsServiceResponse_IfCatalogWithUserExists() + { + int id = 1; + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(true)); + + ServiceResponse serviceResponse = await _service.DeleteCatalogAsync(id); + + _catalogRepositoryMock.Verify(r => r.DeleteAsync(It.IsAny()), Times.Once); + _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Once); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); Assert.True(serviceResponse.Success); - Assert.Equal(serviceResponse.Data, catalogDto); + Assert.Equal(id, serviceResponse.Data); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task DeleteCatalogAsyncCallsMethodOfRepository_And_ReturnsBool(bool flag) + [Fact] + public async Task DeleteCatalogAsyncReturnsUnsuccesfullServiceResponse_IfCatalogWithUserDoesntExist() { int id = 1; - _catalogRepositoryMock.Setup(r => r.DeleteAsync(It.IsAny())).Returns(Task.FromResult(flag)); + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(false)); ServiceResponse serviceResponse = await _service.DeleteCatalogAsync(id); - Assert.Equal(flag, serviceResponse.Success); - if (flag) - _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Once); - else - _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Never); + _catalogRepositoryMock.Verify(r => r.DeleteAsync(It.IsAny()), Times.Never); + _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Never); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); + Assert.False(serviceResponse.Success); + Assert.NotNull(serviceResponse.Message); } [Fact] public async Task GetCatalogsAsyncReturnsIEnumerableCatalogDto() { var catalogs = new Catalog[4]; - _catalogRepositoryMock.Setup(r => r.GetAllAsync()).Returns(Task.FromResult((IEnumerable)catalogs)); + _catalogRepositoryMock.Setup(r => r.GetCatalogsAsync(It.IsAny())).Returns(Task.FromResult((IEnumerable)catalogs)); IEnumerable catalogDtos = await _service.GetCatalogsAsync(); @@ -119,112 +151,191 @@ public async Task GetCatalogsAsyncReturnsIEnumerableCatalogDto() } [Fact] - public async Task UpdateCatalogAsyncCallsMethodOfRepository_And_SavesChanges() + public async Task UpdateCatalogAsyncReturnsUnsuccesfullServiceResponse_IfCatalogWithNameExists() + { + _catalogRepositoryMock.Setup(r => r.HasChildWithNameAsync(It.IsAny())) + .Returns(Task.FromResult(true)); + + ServiceResponse serviceResponse = await _service.UpdateCatalogAsync(new CatalogDto()); + + _catalogRepositoryMock.Verify(r => r.Update(It.IsAny()), Times.Never); + _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Never); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); + Assert.False(serviceResponse.Success); + Assert.NotNull(serviceResponse.Message); + } + + [Fact] + public async Task DeleteImageFromCatalogAsyncCallsMethOfRepositoryAndReturnsServiceResponse_IfCatalogWithUserExists() { - var catalog = new CatalogDto() + var imageCatalog = new UpdateImageCatalogDto() { - Name = "catalog 1", + CatalogId = 1, + ImageId = 1 }; + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(true)); - ServiceResponse serviceResponse = await _service.UpdateCatalogAsync(catalog); + ServiceResponse serviceResponse = await _service.DeleteImageFromCatalogAsync(imageCatalog); - _catalogRepositoryMock.Verify(r => r.Update(It.IsAny()), Times.Once); + _catalogRepositoryMock.Verify(r => r.DeleteImageFromCatalog(It.IsAny()), Times.Once); _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Once); Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); + Assert.True(serviceResponse.Success); + Assert.Equal(imageCatalog, serviceResponse.Data); } [Fact] - public async Task DeleteImageFromCatalogAsyncCallsMethodOfRepository_And_SavesChanges() + public async Task DeleteImageFromCatalogAsyncReturnsUnsuccesfullServiceResponse_IfCatalogWithUserDoesntExist() { var imageCatalog = new UpdateImageCatalogDto() { CatalogId = 1, ImageId = 1 }; - ImageCatalog ic = new ImageCatalog(); + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(false)); - await _service.DeleteImageFromCatalogAsync(imageCatalog); + ServiceResponse serviceResponse = await _service.DeleteImageFromCatalogAsync(imageCatalog); - _catalogRepositoryMock.Verify(r => r.DeleteImageFromCatalog(It.IsAny()), Times.Once); - _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Once); + _catalogRepositoryMock.Verify(r => r.DeleteImageFromCatalog(It.IsAny()), Times.Never); + _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Never); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); + Assert.False(serviceResponse.Success); + Assert.Null(serviceResponse.Data); + Assert.NotNull(serviceResponse.Message); } [Fact] - public async Task GetImagesByCatalogAsyncReturnsPaginationListDtoImages() + public async Task GetImagesByCatalogAsyncCallsMethOfRepositoryAndReturnsServiceResponse_IfCatalogWithUserExists() { int id = 1; int limit = 4; int offset = 0; PaginationListDto paginationList = new PaginationListDto() { Items = new Image[4]}; + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(true)); _catalogRepositoryMock.Setup(r => r.GetImagesByCatalogAsync(id, It.IsAny(), It.IsAny())) .Returns(Task.FromResult(paginationList)); - ServiceResponse> serviceResponse = await _service.GetImagesFromCatalogAsync(id, offset, limit); + ServiceResponse> serviceResponse = await _service.GetImagesFromCatalogAsync(id, offset, limit); _catalogRepositoryMock.Verify(r => r.GetImagesByCatalogAsync(id, offset, limit), Times.Once); Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>>(serviceResponse); + Assert.True(serviceResponse.Success); Assert.Equal(paginationList.Items.Count, serviceResponse.Data.Items.Count); } [Fact] - public async Task AddImageToCatalogAsyncCallsMethodOfRepository_And_SavesChanges() + public async Task GetImagesByCatalogAsyncReturnsUnsuccesfullServiceResponse_IfCatalogWithUserDoesntExist() + { + int id = 1; + int limit = 4; + int offset = 0; + PaginationListDto paginationList = new PaginationListDto() { Items = new Image[4] }; + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(false)); + + ServiceResponse> serviceResponse = await _service.GetImagesFromCatalogAsync(id, offset, limit); + + _catalogRepositoryMock.Verify(r => r.GetImagesByCatalogAsync(id, offset, limit), Times.Never); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>>(serviceResponse); + Assert.False(serviceResponse.Success); + Assert.Null(serviceResponse.Data); + Assert.NotNull(serviceResponse.Message); + } + + [Fact] + public async Task AddImageToCatalogAsyncCallsMethOfRepositoryAndReturnsServiceResponse_IfCatalogWithUserExists() { var updateImageCatalog = new UpdateImageCatalogDto() { CatalogId = 1, ImageId = 1 }; + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(true)); Expression> ex = i => i.ImageId == updateImageCatalog.ImageId && i.CatalogId == updateImageCatalog.CatalogId; - await _service.AddImageToCatalogAsync(updateImageCatalog); + ServiceResponse serviceResponse = await _service.AddImageToCatalogAsync(updateImageCatalog); _catalogRepositoryMock.Verify(r => r.AddImageToCatalog(It.Is(ex)), Times.Once); _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Once); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>(serviceResponse); + Assert.True(serviceResponse.Success); + Assert.Equal(updateImageCatalog, serviceResponse.Data); } [Fact] - public async Task GetSubCatalogsAsyncCallsMethodOfRepository_And_ReturnsCatalogsDto() + public async Task AddImageToCatalogAsyncReturnsUnsuccesfullServiceResponse_IfCatalogWithUserDoesntExist() { - int id = 1; + var updateImageCatalog = new UpdateImageCatalogDto() + { + CatalogId = 1, + ImageId = 1 + }; + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(false)); + Expression> ex = i => i.ImageId == updateImageCatalog.ImageId && i.CatalogId == updateImageCatalog.CatalogId; - ServiceResponse> serviceResponse = await _service.GetSubCatalogsAsync(id); + ServiceResponse serviceResponse = await _service.AddImageToCatalogAsync(updateImageCatalog); + _catalogRepositoryMock.Verify(r => r.AddImageToCatalog(It.Is(ex)), Times.Never); + _dbContextMock.Verify(r => r.SaveChangesAsync(CancellationToken.None), Times.Never); Assert.NotNull(serviceResponse); - _catalogRepositoryMock.Verify(r => r.GetSubCatalogsAsync(id), Times.Once); + Assert.IsAssignableFrom>(serviceResponse); + Assert.False(serviceResponse.Success); + Assert.Null(serviceResponse.Data); + Assert.NotNull(serviceResponse.Message); } [Fact] - public async Task GetCatalogsByUserAsyncCallsMethodOfRepository_And_ReturnsCatalogsDto() + public async Task GetSubCatalogsAsyncCallsMethOfRepositoryAndReturnsServiceResponse_IfCatalogWithUserExists() { - var id = "user id"; + int id = 1; + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(true)); - IEnumerable catalogsDto = await _service.GetCatalogsAsync(id); + ServiceResponse> serviceResponse = await _service.GetSubCatalogsAsync(id); - Assert.NotNull(catalogsDto); - _catalogRepositoryMock.Verify(r => r.GetCatalogsAsync(id), Times.Once); + _catalogRepositoryMock.Verify(r => r.GetSubCatalogsAsync(id), Times.Once); + Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>>(serviceResponse); + Assert.True(serviceResponse.Success); } [Fact] - public async Task GetSubCatalogsByUserAsync_IfCatalogDoesntExist() + public async Task GetSubCatalogsAsyncReturnsUnsuccesfullServiceResponse_IfCatalogWithUserDoesntExist() { - _catalogRepositoryMock.Setup(c => c.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(false)); + int id = 1; + _catalogRepositoryMock.Setup(r => r.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(false)); - ServiceResponse> serviceResponse = await _service.GetSubCatalogsAsync(It.IsAny(), It.IsAny()); + ServiceResponse> serviceResponse = await _service.GetSubCatalogsAsync(id); + _catalogRepositoryMock.Verify(r => r.GetSubCatalogsAsync(id), Times.Never); Assert.NotNull(serviceResponse); + Assert.IsAssignableFrom>>(serviceResponse); Assert.False(serviceResponse.Success); + Assert.Null(serviceResponse.Data); Assert.NotNull(serviceResponse.Message); } [Fact] - public async Task GetSubCatalogsByUserAsync_IfCatalogExists() + public async Task GetCatalogsAsyncCallsMethodOfRepository_And_ReturnsCatalogsDto() { - _catalogRepositoryMock.Setup(c => c.HasCatalogWithUserIdAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(true)); + var id = "user id"; - ServiceResponse> serviceResponse = await _service.GetSubCatalogsAsync(It.IsAny(), It.IsAny()); + IEnumerable catalogsDto = await _service.GetCatalogsAsync(id); - Assert.NotNull(serviceResponse); - Assert.True(serviceResponse.Success); + Assert.NotNull(catalogsDto); + _catalogRepositoryMock.Verify(r => r.GetCatalogsAsync(id), Times.Once); } } } diff --git a/ImageBase.WebApp/Controllers/PrivateCatalogsController.cs b/ImageBase.WebApp/Controllers/PrivateCatalogsController.cs index 81a65f4..a181490 100644 --- a/ImageBase.WebApp/Controllers/PrivateCatalogsController.cs +++ b/ImageBase.WebApp/Controllers/PrivateCatalogsController.cs @@ -48,9 +48,9 @@ public async Task>> GetSubCatalogsAsync(int } [HttpGet("image/{id:int}")] - public async Task>> GetImagesByCatalog(int id, int offset = 0, int limit = 4) + public async Task>> GetImagesByCatalog(int id, int offset = 0, int limit = 4) { - ServiceResponse> serviceGetImagesByCatalog = await _catalogService.GetImagesFromCatalogAsync(id, offset, limit, await CurrentUser()); + ServiceResponse> serviceGetImagesByCatalog = await _catalogService.GetImagesFromCatalogAsync(id, offset, limit, await CurrentUser()); if (serviceGetImagesByCatalog.Success == false) { return Forbid(serviceGetImagesByCatalog.Message); diff --git a/ImageBase.WebApp/Controllers/PublicCatalogsController.cs b/ImageBase.WebApp/Controllers/PublicCatalogsController.cs index dc34c10..a31894d 100644 --- a/ImageBase.WebApp/Controllers/PublicCatalogsController.cs +++ b/ImageBase.WebApp/Controllers/PublicCatalogsController.cs @@ -51,9 +51,9 @@ public async Task>> GetSubCatalogsAsync(int } [HttpGet("image/{id:int}")] - public async Task>> GetImagesByCatalog(int id, int offset = 0, int limit = 4) + public async Task>> GetImagesByCatalog(int id, int offset = 0, int limit = 4) { - ServiceResponse> serviceGetImagesByCatalog = await _catalogService.GetImagesFromCatalogAsync(id, offset, limit); + ServiceResponse> serviceGetImagesByCatalog = await _catalogService.GetImagesFromCatalogAsync(id, offset, limit); if (serviceGetImagesByCatalog.Success == false) { return Forbid(serviceGetImagesByCatalog.Message); diff --git a/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/CatalogConfiguration.cs b/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/CatalogConfiguration.cs index 37b8f45..10b7b96 100644 --- a/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/CatalogConfiguration.cs +++ b/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/CatalogConfiguration.cs @@ -15,6 +15,7 @@ public CatalogConfiguration(EntityTypeBuilder entityBuilder) entityBuilder.HasKey(c => c.Id); entityBuilder.Property(c => c.Id).HasColumnName("id"); entityBuilder.Property(c => c.Name).HasColumnName("name").HasMaxLength(30).IsRequired(); + entityBuilder.HasOne(c => c.ParentCatalog).WithMany().OnDelete(DeleteBehavior.Cascade); entityBuilder.Property(c => c.ParentCatalogId).HasColumnName("parent_catalog_id"); entityBuilder.Property(c => c.UserId).HasColumnName("user_id"); entityBuilder.Property(c => c.Id).ValueGeneratedOnAdd(); diff --git a/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/ImageConfiguration.cs b/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/ImageConfiguration.cs index 5f04ee5..193d6c9 100644 --- a/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/ImageConfiguration.cs +++ b/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/ImageConfiguration.cs @@ -17,8 +17,18 @@ public ImageConfiguration(EntityTypeBuilder entityBuilder) entityBuilder.Property(i => i.Title).HasColumnName("title").HasMaxLength(200).IsRequired(); entityBuilder.Property(i => i.Description).HasColumnName("description").HasColumnType("text"); entityBuilder.Property(i => i.KeyWords).HasColumnName("key_words").HasColumnType("text"); + entityBuilder.Property(i => i.ServiceId).HasColumnName("service_id").IsRequired(); + entityBuilder.Property(i => i.ExternalId).HasColumnName("external_id").HasColumnType("text"); + entityBuilder.Property(i => i.FileSize).HasColumnName("file_size").IsRequired(); + entityBuilder.Property(i => i.Height).HasColumnName("height").IsRequired(); + entityBuilder.Property(i => i.Width).HasColumnName("width").IsRequired(); + entityBuilder.Property(i => i.LargePreviewUrl).HasColumnName("large_preview_url").HasColumnType("text").IsRequired(); + entityBuilder.Property(i => i.SmallPreviewUrl).HasColumnName("small_preview_url").HasColumnType("text").IsRequired(); + entityBuilder.Property(i => i.OriginalUrl).HasColumnName("original_url").HasColumnType("text").IsRequired(); entityBuilder.Property(i => i.Id).ValueGeneratedOnAdd(); + entityBuilder.HasIndex(i => new { i.ServiceId, i.ExternalId}).IsUnique(); + entityBuilder.ToTable("images"); } } diff --git a/ImageBase.WebApp/Data/Dtos/AddImageDto.cs b/ImageBase.WebApp/Data/Dtos/AddImageDto.cs index b324153..67b7967 100644 --- a/ImageBase.WebApp/Data/Dtos/AddImageDto.cs +++ b/ImageBase.WebApp/Data/Dtos/AddImageDto.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; @@ -7,6 +8,8 @@ namespace ImageBase.WebApp.Data.Dtos { public class AddImageDto { + [Required] + [StringLength(200, MinimumLength = 1, ErrorMessage = "Длина строки должна быть от 1 до 200 символов")] public string Title { get; set; } public string Description { get; set; } public string KeyWords { get; set; } diff --git a/ImageBase.WebApp/Data/Dtos/ImageDto.cs b/ImageBase.WebApp/Data/Dtos/ImageDto.cs index 009880c..67c3ab0 100644 --- a/ImageBase.WebApp/Data/Dtos/ImageDto.cs +++ b/ImageBase.WebApp/Data/Dtos/ImageDto.cs @@ -9,8 +9,6 @@ namespace ImageBase.WebApp.Data.Dtos public class ImageDto { public long Id { get; set; } - [Required] - [StringLength(200, MinimumLength = 1, ErrorMessage = "Длина строки должна быть от 1 до 200 символов")] public string Title { get; set; } public string Description { get; set; } } diff --git a/ImageBase.WebApp/Data/Dtos/ImagesListItemDto.cs b/ImageBase.WebApp/Data/Dtos/ImagesListItemDto.cs new file mode 100644 index 0000000..247673d --- /dev/null +++ b/ImageBase.WebApp/Data/Dtos/ImagesListItemDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ImageBase.WebApp.Data.Dtos +{ + public class ImagesListItemDto + { + public long Id { get; set; } + public string Title { get; set; } + public string SmallPreviewUrl { get; set; } + public string LargePreviewUrl { get; set; } + public string OriginalUrl { get; set; } + } +} diff --git a/ImageBase.WebApp/Data/Models/Image.cs b/ImageBase.WebApp/Data/Models/Image.cs index 556e705..2d01673 100644 --- a/ImageBase.WebApp/Data/Models/Image.cs +++ b/ImageBase.WebApp/Data/Models/Image.cs @@ -10,6 +10,14 @@ public class Image: BaseEntity public string Title { get; set; } public string Description { get; set; } public string KeyWords { get; set; } + public int ServiceId { get; set; } + public string ExternalId { get; set; } + public string SmallPreviewUrl { get; set; } + public string LargePreviewUrl { get; set; } + public string OriginalUrl { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public int FileSize { get; set; } public List ImageCatalogs { get; set; } } diff --git a/ImageBase.WebApp/Data/Profiles/CatalogProfile.cs b/ImageBase.WebApp/Data/Profiles/CatalogProfile.cs index 23eda86..ad323f2 100644 --- a/ImageBase.WebApp/Data/Profiles/CatalogProfile.cs +++ b/ImageBase.WebApp/Data/Profiles/CatalogProfile.cs @@ -19,6 +19,7 @@ public CatalogProfile() CreateMap() .ForMember(map => map.CatalogId, map => map.MapFrom(c => c)); CreateMap, PaginationListDto>(); + CreateMap, PaginationListDto>(); } } } diff --git a/ImageBase.WebApp/Migrations/20201203144604_Init_12_3.Designer.cs b/ImageBase.WebApp/Migrations/20201203144604_Init_12_3.Designer.cs new file mode 100644 index 0000000..21d5270 --- /dev/null +++ b/ImageBase.WebApp/Migrations/20201203144604_Init_12_3.Designer.cs @@ -0,0 +1,447 @@ +// +using System; +using ImageBase.WebApp.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace ImageBase.WebApp.Migrations +{ + [DbContext(typeof(AspPostgreSQLContext))] + [Migration("20201203144604_Init_12_3")] + partial class Init_12_3 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.Authentication.User", b => + { + b.Property("Id") + .HasColumnName("id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnName("access_failed_count") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("concurrency_stamp") + .HasColumnType("text"); + + b.Property("Email") + .HasColumnName("email") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .HasColumnName("email_confirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnName("lockout_enabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnName("lockout_end") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasColumnName("normalized_email") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasColumnName("normalized_user_name") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("password_hash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnName("phone_number") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnName("phone_number_confirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnName("security_stamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnName("two_factor_enabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasColumnName("user_name") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.Catalog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .IsRequired() + .HasColumnName("name") + .HasColumnType("character varying(30)") + .HasMaxLength(30); + + b.Property("ParentCatalogId") + .HasColumnName("parent_catalog_id") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnName("user_id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ParentCatalogId"); + + b.HasIndex("UserId"); + + b.ToTable("catalogs"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Description") + .HasColumnName("description") + .HasColumnType("text"); + + b.Property("ExternalId") + .HasColumnName("external_id") + .HasColumnType("text"); + + b.Property("FileSize") + .HasColumnName("file_size") + .HasColumnType("integer"); + + b.Property("Height") + .HasColumnName("height") + .HasColumnType("integer"); + + b.Property("KeyWords") + .HasColumnName("key_words") + .HasColumnType("text"); + + b.Property("LargePreviewUrl") + .IsRequired() + .HasColumnName("large_preview_url") + .HasColumnType("text"); + + b.Property("OriginalUrl") + .IsRequired() + .HasColumnName("original_url") + .HasColumnType("text"); + + b.Property("ServiceId") + .HasColumnName("service_id") + .HasColumnType("integer"); + + b.Property("SmallPreviewUrl") + .HasColumnName("small_preview_url") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnName("title") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.Property("Width") + .HasColumnName("width") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ServiceId", "ExternalId") + .IsUnique(); + + b.ToTable("images"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.ImageCatalog", b => + { + b.Property("CatalogId") + .HasColumnName("catalog_id") + .HasColumnType("integer"); + + b.Property("ImageId") + .HasColumnName("image_id") + .HasColumnType("bigint"); + + b.HasKey("CatalogId", "ImageId"); + + b.HasIndex("ImageId"); + + b.ToTable("images_catalogs"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnName("id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("concurrency_stamp") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnName("role_name") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasColumnName("normalized_role_name") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("roles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ClaimType") + .HasColumnName("claim_type") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnName("claim_value") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnName("role_id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("roles_claims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ClaimType") + .HasColumnName("claim_type") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnName("claim_value") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnName("user_id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("users_claims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnName("login_provider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnName("provider_key") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnName("provider_display_name") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnName("user_id") + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("users_logins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnName("users_id") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnName("roles_id") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("users_roles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnName("id") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnName("login_provider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnName("name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnName("value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("users_tokens"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.Catalog", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Catalog", "ParentCatalog") + .WithMany() + .HasForeignKey("ParentCatalogId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", "User") + .WithMany("Catalogs") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.ImageCatalog", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Catalog", "Catalog") + .WithMany("ImageCatalogs") + .HasForeignKey("CatalogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ImageBase.WebApp.Data.Models.Image", "Image") + .WithMany("ImageCatalogs") + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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("ImageBase.WebApp.Data.Models.Authentication.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Authentication.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("ImageBase.WebApp.Data.Models.Authentication.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ImageBase.WebApp/Migrations/20201203144604_Init_12_3.cs b/ImageBase.WebApp/Migrations/20201203144604_Init_12_3.cs new file mode 100644 index 0000000..594356a --- /dev/null +++ b/ImageBase.WebApp/Migrations/20201203144604_Init_12_3.cs @@ -0,0 +1,130 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ImageBase.WebApp.Migrations +{ + public partial class Init_12_3 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_catalogs_catalogs_parent_catalog_id", + table: "catalogs"); + + migrationBuilder.AddColumn( + name: "external_id", + table: "images", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "file_size", + table: "images", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "height", + table: "images", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "large_preview_url", + table: "images", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "original_url", + table: "images", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "service_id", + table: "images", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "small_preview_url", + table: "images", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "width", + table: "images", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + name: "IX_images_service_id_external_id", + table: "images", + columns: new[] { "service_id", "external_id" }, + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_catalogs_catalogs_parent_catalog_id", + table: "catalogs", + column: "parent_catalog_id", + principalTable: "catalogs", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_catalogs_catalogs_parent_catalog_id", + table: "catalogs"); + + migrationBuilder.DropIndex( + name: "IX_images_service_id_external_id", + table: "images"); + + migrationBuilder.DropColumn( + name: "external_id", + table: "images"); + + migrationBuilder.DropColumn( + name: "file_size", + table: "images"); + + migrationBuilder.DropColumn( + name: "height", + table: "images"); + + migrationBuilder.DropColumn( + name: "large_preview_url", + table: "images"); + + migrationBuilder.DropColumn( + name: "original_url", + table: "images"); + + migrationBuilder.DropColumn( + name: "service_id", + table: "images"); + + migrationBuilder.DropColumn( + name: "small_preview_url", + table: "images"); + + migrationBuilder.DropColumn( + name: "width", + table: "images"); + + migrationBuilder.AddForeignKey( + name: "FK_catalogs_catalogs_parent_catalog_id", + table: "catalogs", + column: "parent_catalog_id", + principalTable: "catalogs", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/ImageBase.WebApp/Migrations/AspPostgreSQLContextModelSnapshot.cs b/ImageBase.WebApp/Migrations/AspPostgreSQLContextModelSnapshot.cs index 0e38d17..ff6f84d 100644 --- a/ImageBase.WebApp/Migrations/AspPostgreSQLContextModelSnapshot.cs +++ b/ImageBase.WebApp/Migrations/AspPostgreSQLContextModelSnapshot.cs @@ -141,18 +141,55 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnName("description") .HasColumnType("text"); + b.Property("ExternalId") + .HasColumnName("external_id") + .HasColumnType("text"); + + b.Property("FileSize") + .HasColumnName("file_size") + .HasColumnType("integer"); + + b.Property("Height") + .HasColumnName("height") + .HasColumnType("integer"); + b.Property("KeyWords") .HasColumnName("key_words") .HasColumnType("text"); + b.Property("LargePreviewUrl") + .IsRequired() + .HasColumnName("large_preview_url") + .HasColumnType("text"); + + b.Property("OriginalUrl") + .IsRequired() + .HasColumnName("original_url") + .HasColumnType("text"); + + b.Property("ServiceId") + .HasColumnName("service_id") + .HasColumnType("integer"); + + b.Property("SmallPreviewUrl") + .HasColumnName("small_preview_url") + .HasColumnType("text"); + b.Property("Title") .IsRequired() .HasColumnName("title") .HasColumnType("character varying(200)") .HasMaxLength(200); + b.Property("Width") + .HasColumnName("width") + .HasColumnType("integer"); + b.HasKey("Id"); + b.HasIndex("ServiceId", "ExternalId") + .IsUnique(); + b.ToTable("images"); }); @@ -329,8 +366,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("ImageBase.WebApp.Data.Models.Catalog", "ParentCatalog") .WithMany() - .HasForeignKey("ParentCatalogId") - .OnDelete(DeleteBehavior.Cascade); + .HasForeignKey("ParentCatalogId"); b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", "User") .WithMany("Catalogs") diff --git a/ImageBase.WebApp/Services/Implementations/CatalogService.cs b/ImageBase.WebApp/Services/Implementations/CatalogService.cs index c38d115..48f53db 100644 --- a/ImageBase.WebApp/Services/Implementations/CatalogService.cs +++ b/ImageBase.WebApp/Services/Implementations/CatalogService.cs @@ -121,6 +121,7 @@ public async Task> DeleteImageFromCatalog ImageCatalog imageCatalog = await _repository.GetImageCatalogByIdFKAsync(imageCatalogDto.ImageId, imageCatalogDto.CatalogId); _repository.DeleteImageFromCatalog(imageCatalog); await _context.SaveChangesAsync(); + serviceResponse.Data = imageCatalogDto; } else { @@ -131,14 +132,14 @@ public async Task> DeleteImageFromCatalog return serviceResponse; } - public async Task>> GetImagesFromCatalogAsync(int id, int offset, int limit, string userId = null) + public async Task>> GetImagesFromCatalogAsync(int id, int offset, int limit, string userId = null) { - ServiceResponse> serviceResponse = new ServiceResponse>(); + ServiceResponse> serviceResponse = new ServiceResponse>(); if (await _repository.HasCatalogWithUserIdAsync(id, userId)) { PaginationListDto paginationListDto = await _repository.GetImagesByCatalogAsync(id, offset, limit); - serviceResponse.Data = _mapper.Map, PaginationListDto>(paginationListDto); + serviceResponse.Data = _mapper.Map, PaginationListDto>(paginationListDto); } else { diff --git a/ImageBase.WebApp/Services/Interfaces/ICatalogService.cs b/ImageBase.WebApp/Services/Interfaces/ICatalogService.cs index 7857a16..e51c75d 100644 --- a/ImageBase.WebApp/Services/Interfaces/ICatalogService.cs +++ b/ImageBase.WebApp/Services/Interfaces/ICatalogService.cs @@ -14,7 +14,7 @@ public interface ICatalogService Task> GetCatalogAsync(int id, string userId = null); Task> UpdateCatalogAsync(CatalogDto catalogDto); Task> DeleteImageFromCatalogAsync(UpdateImageCatalogDto imageCatalogDto, string userId = null); - Task>> GetImagesFromCatalogAsync(int id, int offset, int limit, string userId = null); + Task>> GetImagesFromCatalogAsync(int id, int offset, int limit, string userId = null); Task> AddImageToCatalogAsync(UpdateImageCatalogDto imageCatalogDto, string userId = null); Task> GetCatalogsAsync(string userId = null); Task>> GetSubCatalogsAsync(int parentId, string userId = null);