diff --git a/HomeWork31/HomeWork31/Dockerfile b/HomeWork31/HomeWork31/Dockerfile
new file mode 100644
index 0000000..c92cff2
--- /dev/null
+++ b/HomeWork31/HomeWork31/Dockerfile
@@ -0,0 +1,25 @@
+#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
+
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
+USER app
+WORKDIR /app
+EXPOSE 8080
+EXPOSE 8081
+
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+ARG BUILD_CONFIGURATION=Release
+WORKDIR /src
+COPY ["HomeWork31.csproj", "."]
+RUN dotnet restore "./././HomeWork31.csproj"
+COPY . .
+WORKDIR "/src/."
+RUN dotnet build "./HomeWork31.csproj" -c $BUILD_CONFIGURATION -o /app/build
+
+FROM build AS publish
+ARG BUILD_CONFIGURATION=Release
+RUN dotnet publish "./HomeWork31.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "HomeWork31.dll"]
\ No newline at end of file
diff --git a/HomeWork34/.dockerignore b/HomeWork34/.dockerignore
new file mode 100644
index 0000000..cd967fc
--- /dev/null
+++ b/HomeWork34/.dockerignore
@@ -0,0 +1,25 @@
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/.idea
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/HomeWork34/.editorconfig b/HomeWork34/.editorconfig
new file mode 100644
index 0000000..d89f68e
--- /dev/null
+++ b/HomeWork34/.editorconfig
@@ -0,0 +1,68 @@
+[*.cs]
+
+# Default severity for analyzer diagnostics with category 'Usage'
+dotnet_analyzer_diagnostic.category-Usage.severity = none
+csharp_indent_labels = one_less_than_current
+csharp_using_directive_placement = outside_namespace:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_prefer_braces = true:silent
+csharp_style_namespace_declarations = block_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_space_around_binary_operators = before_and_after
+
+[*.{cs,vb}]
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+indent_size = 4
+end_of_line = crlf
diff --git a/HomeWork34/.gitignore b/HomeWork34/.gitignore
new file mode 100644
index 0000000..a5dbce6
--- /dev/null
+++ b/HomeWork34/.gitignore
@@ -0,0 +1,288 @@
+syntax glob
+
+[Bb]inaries
+
+# Build tools related files
+[Tt]ools
+
+### VisualStudio ###
+
+# User-specific files
+.suo
+.user
+.userosscache
+.sln.docstates
+
+# Build results
+[Dd]ebug
+[Dd]ebugPublic
+[Rr]elease
+[Rr]eleases
+x64
+x86
+build
+bld
+[Bb]in
+[Oo]bj
+msbuild.log
+
+# Visual Studio 2015
+.vs
+
+# Visual Studio 2015 Pre-CTP6
+.sln.ide
+.ide
+
+# MSTest test Results
+[Tt]est[Rr]esult
+[Bb]uild[Ll]og.
+
+#NUNIT
+.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS
+[Rr]eleasePS
+dlldata.c
+
+_i.c
+_p.c
+_i.h
+.ilk
+.meta
+.obj
+.pch
+.pdb
+.pgc
+.pgd
+.rsp
+.sbr
+.tlb
+.tli
+.tlh
+.tmp
+.tmp_proj
+.log
+.html
+.vspscc
+.vssscc
+.builds
+.pidb
+.svclog
+.scc
+
+# Chutzpah Test files
+_Chutzpah
+
+# Visual C++ cache files
+ipch
+.aps
+.ncb
+.opensdf
+.sdf
+.cachefile
+
+# Visual Studio profiler
+.psess
+.vsp
+.vspx
+
+# TFS 2012 Local Workspace
+$tf
+
+# Guidance Automation Toolkit
+.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper
+.[Rr]e[Ss]harper
+.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity
+
+# DotCover is a Code Coverage Tool
+.dotCover
+
+# NCrunch
+_NCrunch_
+.crunch.local.xml
+
+# MightyMoose
+.mm.
+AutoTest.Net
+
+# Web workbench (sass)
+.sass-cache
+
+# Installshield output folder
+[Ee]xpress
+
+# DocProject is a documentation generator add-in
+DocProjectbuildhelp
+DocProjectHelp.HxT
+DocProjectHelp.HxC
+DocProjectHelp.hhc
+DocProjectHelp.hhk
+DocProjectHelp.hhp
+DocProjectHelpHtml2
+DocProjectHelphtml
+
+# Click-Once directory
+publish
+
+# Publish Web Output
+.[Pp]ublish.xml
+.azurePubxml
+.pubxml
+.publishproj
+
+# NuGet Packages
+.nupkg
+packages
+project.lock.json
+
+# Windows Azure Build Output
+csx
+.build.csdef
+
+# Windows Store app package directory
+AppPackages
+
+# Others
+sql
+.Cache
+ClientBin
+[Ss]tyle[Cc]op.
+~$
+.dbmdl
+.dbproj.schemaview
+.pfx
+.publishsettings
+node_modules
+.metaproj
+.metaproj.tmp
+.atom-build.json
+tags
+TAGS
+
+# RIASilverlight projects
+Generated_Code
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files
+Backup
+UpgradeLog.XML
+UpgradeLog.htm
+
+# SQL Server files
+.mdf
+.ldf
+
+# Business Intelligence projects
+.rdl.data
+.bim.layout
+.bim_.settings
+
+# Microsoft Fakes
+FakesAssemblies
+
+### MonoDevelop ###
+
+.pidb
+.userprefs
+
+### Windows ###
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN
+
+# Windows Installer files
+.cab
+.msi
+.msm
+.msp
+
+# Windows shortcuts
+.lnk
+
+# Common binary extensions on Windows
+.exe
+.dll
+.lib
+
+### Linux ###
+
+~
+##
+
+# KDE directory preferences
+.directory
+
+### OSX ###
+
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two r
+Icon
+
+# Thumbnails
+._
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# We have some checked in prebuilt generated files
+!srcpalprebuiltidl_i.c
+
+# Valid 'debug' folder, that contains CLR debuggin code
+!srcdebug
+
+# Ignore folders created by the test build
+TestWrappers_x64_debug
+TestWrappers_x64_checked
+TestWrappers_x64_release
+
+Vagrantfile
+.vagrant
+
+# CMake files
+CMakeFiles
+cmake_install.cmake
+CMakeCache.txt
+Makefile
+
+# Cross compilation
+crossrootfs
+
+#python import files
+.pyc
+
+.idea
+.generated-resources
+.terraform
+terraform.tfstate
+terraform.tfstate.backup
\ No newline at end of file
diff --git a/HomeWork34/Catalog.UnitTest/Catalog.UnitTest.csproj b/HomeWork34/Catalog.UnitTest/Catalog.UnitTest.csproj
new file mode 100644
index 0000000..a8fd58f
--- /dev/null
+++ b/HomeWork34/Catalog.UnitTest/Catalog.UnitTest.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HomeWork34/Catalog.UnitTest/GlobalUsing.cs b/HomeWork34/Catalog.UnitTest/GlobalUsing.cs
new file mode 100644
index 0000000..bb725b9
--- /dev/null
+++ b/HomeWork34/Catalog.UnitTest/GlobalUsing.cs
@@ -0,0 +1,15 @@
+global using System;
+global using System.Collections.Generic;
+global using System.Threading.Tasks;
+global using AutoMapper;
+global using Catalog.Host.Data;
+global using Catalog.Host.Repositories.Interfaces;
+global using Catalog.Host.Services;
+global using Catalog.Host.Services.Interfaces;
+global using FluentAssertions;
+global using Infrastructure.Services.Interfaces;
+global using Microsoft.EntityFrameworkCore.Storage;
+global using Microsoft.Extensions.Logging;
+global using Moq;
+global using Xunit;
+global using Infrastructure.Services;
\ No newline at end of file
diff --git a/HomeWork34/Catalog.UnitTest/Services/CatalogBrandServiceTest.cs b/HomeWork34/Catalog.UnitTest/Services/CatalogBrandServiceTest.cs
new file mode 100644
index 0000000..8bd4574
--- /dev/null
+++ b/HomeWork34/Catalog.UnitTest/Services/CatalogBrandServiceTest.cs
@@ -0,0 +1,191 @@
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Models.Response;
+using Infrastructure.Exeptions;
+using Infrastructure.Models;
+
+
+namespace Catalog.UnitTest.Services
+{
+ public class CatalogBrandServiceTest
+ {
+ private readonly ICatalogBrandService _catalogBrandService;
+
+ private readonly Mock _brandRepository;
+ private readonly Mock> _Logger;
+ private readonly Mock _mapper;
+ private readonly Mock> _dbContextWrapper;
+ public CatalogBrandServiceTest()
+ {
+ _brandRepository = new Mock();
+ _dbContextWrapper = new Mock>();
+ _mapper = new Mock();
+ var dbContextTransaction = new Mock();
+ _dbContextWrapper.Setup(x => x.BeginTransactionAsync(CancellationToken.None)).ReturnsAsync(dbContextTransaction.Object);
+ _Logger = new Mock>();
+
+ _catalogBrandService = new CatalogBrandService(_dbContextWrapper.Object, _Logger.Object, _brandRepository.Object, _mapper.Object);
+ }
+
+ [Fact]
+ public async Task Add_Seccusfull()
+ {
+ //arrage
+ var outTest = 1;
+ string inTest = "test";
+
+ _brandRepository
+ .Setup(s => s.AddAsync(It.Is(i => i == inTest)))
+ .ReturnsAsync(outTest);
+
+ //act
+ var responce = await _catalogBrandService.AddAsync(inTest);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.Id.Should().Be(outTest);
+ responce.Id.Should().NotBeNull();
+ responce.ErrorMessage.Should().BeNull();
+ responce.RespCode.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task Add_Failed()
+ {
+ //arrage
+ int? outTest = null;
+ string? inTest = null;
+
+ _brandRepository
+ .Setup(s => s.AddAsync(It.IsAny()))
+ .ReturnsAsync(outTest);
+
+ //act
+ var responce = await _catalogBrandService.AddAsync(inTest);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.Id.Should().BeNull();
+ responce.ErrorMessage.Should().NotBeNull();
+ responce.RespCode.Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task Delete_Succesfull()
+ {
+ //arrage
+ int inTest = 1;
+ string? outTest = "test";
+
+ _brandRepository
+ .Setup(s=>s.DeleteAsync(It.IsAny()))
+ .ReturnsAsync(outTest);
+
+ //act
+ var responce = await _catalogBrandService.DeleteAsync(inTest);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.ErrorMessage.Should().BeNull();
+ responce.RespCode.Should().BeNull();
+ responce.Status.Should().NotBeEmpty();
+ responce.Status.Should().NotBeNull();
+ responce.Status.Should().Be(outTest);
+ }
+
+ [Fact]
+ public async Task Delete_Failed()
+ {
+ //arrage
+ int? inTest = null;
+ string? outTest = "test";
+
+ _brandRepository
+ .Setup(s => s.DeleteAsync(It.IsAny()))
+ .ReturnsAsync(outTest);
+
+ //act
+ var responce = await _catalogBrandService.DeleteAsync(inTest!);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.ErrorMessage.Should().NotBeNull();
+ responce.RespCode.Should().NotBeNull();
+ responce.Status.Should().BeNull();
+ }
+
+ [Theory]
+ [InlineData("test")]
+ public async Task Update_Succesfull(string input)
+ {
+ //arrage
+ var dto = new CatalogBrandDto()
+ {
+ Brand = input
+ };
+
+ var entity = new CatalogBrand()
+ {
+ Brand = input
+ };
+
+ _mapper
+ .Setup(s =>
+ s.Map(It.Is(i =>
+ i.Equals(dto))))
+ .Returns(entity);
+
+ _brandRepository
+ .Setup(s =>
+ s.UpdateAsync(It.Is(s=>
+ s.Equals(entity))))
+ .ReturnsAsync(entity);
+
+ _mapper
+ .Setup(s=>
+ s.Map(It.Is(i=>
+ i.Equals(entity))))
+ .Returns(dto);
+
+ //act
+ var responce = await _catalogBrandService.UpdateAsync(dto);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.ErrorMessage.Should().BeNull();
+ responce.RespCode.Should().BeNull();
+ responce.UpdataModel.Should().NotBeNull();
+ responce.UpdataModel.Should().BeSameAs(dto);
+ }
+
+ [Fact]
+ public async Task Update_Failed()
+ {
+ //arrage
+ CatalogBrandDto? dto = null;
+
+ CatalogBrand? entity = null;
+
+ _mapper.Setup(s =>
+ s.Map(It.Is(i =>
+ i.Equals(dto)))).Returns(entity!);
+
+ _brandRepository
+ .Setup(s => s.UpdateAsync(It.IsAny()))
+ .ReturnsAsync(entity);
+
+ _mapper.Setup(s =>
+ s.Map(It.Is(i =>
+ i.Equals(entity)))).Returns(dto!);
+
+ //act
+ var responce = await _catalogBrandService.UpdateAsync(dto);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.ErrorMessage.Should().NotBeNull();
+ responce.RespCode.Should().NotBeNull();
+ responce.UpdataModel.Should().BeNull();
+ }
+ }
+}
diff --git a/HomeWork34/Catalog.UnitTest/Services/CatalogServiceTest.cs b/HomeWork34/Catalog.UnitTest/Services/CatalogServiceTest.cs
new file mode 100644
index 0000000..d3049e4
--- /dev/null
+++ b/HomeWork34/Catalog.UnitTest/Services/CatalogServiceTest.cs
@@ -0,0 +1,152 @@
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Models.enums;
+using Catalog.Host.Models.Response;
+
+namespace Catalog.UnitTest.Services
+{
+ public class CatalogServiceTest
+ {
+ private readonly ICatalogService _serviceCatalog;
+
+ private readonly Mock _catalogRepository;
+ private readonly Mock _mapper;
+ private readonly Mock> _dbContextWrapper;
+ private readonly Mock> _logger;
+
+ public CatalogServiceTest()
+ {
+ _catalogRepository = new Mock();
+ _mapper = new Mock();
+ _dbContextWrapper = new Mock>();
+ _logger = new Mock>();
+
+ var dbContextTransaction = new Mock();
+ _dbContextWrapper.Setup(s => s.BeginTransactionAsync(CancellationToken.None)).ReturnsAsync(dbContextTransaction.Object);
+
+ _serviceCatalog = new CatalogService(_dbContextWrapper.Object, _logger.Object, _catalogRepository.Object, _mapper.Object);
+ }
+
+ [Fact]
+ public async Task GetByPageAsync_Succusfull()
+ {
+ //arrage
+ int pageSizeTest = 1;
+ int pageIndexTest = 5;
+ int totalCountTest = 12;
+ int brandFilter = 1;
+ int typeFilter = 1;
+ var filter = new Dictionary()
+ {
+ [CatalogTypeFilter.Brand] = 1,
+ [CatalogTypeFilter.Type] = 1
+ };
+
+ var paginationItemReponceSeccusfull = new PaginatedItems()
+ {
+ TotalCount = totalCountTest,
+ Data = new List()
+ {
+ new CatalogItem()
+ {
+ Name ="Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrandId = 1,
+ CatalogTypeId = 1,
+ PictureFileName = "Test"
+ },
+ new CatalogItem()
+ {
+ Name ="Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrandId = 1,
+ CatalogTypeId = 1,
+ PictureFileName = "Test"
+ },
+ new CatalogItem()
+ {
+ Name ="Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrandId = 1,
+ CatalogTypeId = 1,
+ PictureFileName = "Test"
+ },
+ }
+ };
+
+ var catalogItemDtoSuccesfull = new CatalogItemDto()
+ {
+ Name = "Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrand = new CatalogBrandDto() { Id = 1, Brand = "TEst" },
+ CatalogType = new CatalogTypeDto() { Id = 1, Type = "TEst" },
+ PictureUrl = "Test"
+ };
+
+ var catalogItemSuccesfull = new CatalogItem()
+ {
+ Name = "Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrandId = 1,
+ CatalogTypeId = 1,
+ PictureFileName = "Test"
+ };
+
+ _catalogRepository.Setup(s => s.GetByPageAsync(
+ It.Is(i => i == pageIndexTest),
+ It.Is(i => i == pageSizeTest),
+ It.Is(i=> i == brandFilter),
+ It.Is(i=>i == typeFilter)
+ ))
+ .ReturnsAsync(paginationItemReponceSeccusfull);
+
+ _mapper.Setup(s => s.Map(
+ It.Is(i => i.Equals(catalogItemSuccesfull))))
+ .Returns(catalogItemDtoSuccesfull); // check mapper failed
+
+ //act
+ var reesponce = await _serviceCatalog.GetByPageAsync(pageSizeTest, pageIndexTest, filter);
+
+ //assert
+ reesponce.Should().NotBeNull();
+ reesponce.Count.Should().Be(totalCountTest);
+ reesponce.Data.Should().NotBeNull();
+ //reesponce.Data.First().Should().NotBeNull();
+ reesponce.PageIndex.Should().Be(pageIndexTest);
+ reesponce.PageSize.Should().Be(pageSizeTest);
+ }
+
+ [Fact]
+ public async Task GetByPageAsync_Failed()
+ {
+ //arrage
+ var pageIndexTest = 2000;
+ var pageSizeTest = 1000;
+ int? typeFilter = null;
+ int? brandFilter = null;
+
+ _catalogRepository.Setup(s => s.GetByPageAsync(
+ It.Is(i => i == pageIndexTest),
+ It.Is(i => i == pageSizeTest),
+ It.Is(i=>i == typeFilter),
+ It.Is(i=>i== brandFilter)))
+ .Returns((Func>)null!);
+
+ //act
+ var responce = await _serviceCatalog.GetByPageAsync(pageSizeTest, pageIndexTest, null);
+
+ //assert
+ responce.Should().NotBeNull();
+ }
+ }
+}
diff --git a/HomeWork34/Catalog.UnitTest/Services/CatalogTypeServiceTest.cs b/HomeWork34/Catalog.UnitTest/Services/CatalogTypeServiceTest.cs
new file mode 100644
index 0000000..1f3be7c
--- /dev/null
+++ b/HomeWork34/Catalog.UnitTest/Services/CatalogTypeServiceTest.cs
@@ -0,0 +1,198 @@
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+
+namespace Catalog.UnitTest.Services
+{
+ public class CatalogTypeServiceTest
+ {
+ private readonly Mock _catalogTypeRepository;
+ private readonly Mock> _logger;
+ private readonly Mock> _dbContextWrapper;
+ private readonly Mock _mapper;
+
+ private readonly CatalogTypeService _catalogTypeService;
+
+ public CatalogTypeServiceTest()
+ {
+ _catalogTypeRepository = new Mock();
+ _logger = new Mock>();
+ _mapper = new Mock();
+ _dbContextWrapper = new Mock>();
+
+ var dbContextTransaction = new Mock();
+ _dbContextWrapper
+ .Setup(s => s.BeginTransactionAsync(CancellationToken.None))
+ .ReturnsAsync(dbContextTransaction.Object);
+
+ _catalogTypeService = new CatalogTypeService(
+ _catalogTypeRepository.Object,
+ _mapper.Object,
+ _dbContextWrapper.Object,
+ _logger.Object);
+ }
+
+ [Fact]
+ public async Task Add_Seccusfull()
+ {
+ //arrage
+ var inTest = "test";
+ var outTest = 1;
+
+ _catalogTypeRepository
+ .Setup(s =>
+ s.AddTypeAsync(It.IsAny()))
+ .ReturnsAsync(outTest);
+
+ //act
+ var responce = await _catalogTypeService.AddType(inTest);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.Id.Should().Be(outTest);
+ responce.Id.Should().NotBeNull();
+ responce.ErrorMessage.Should().BeNull();
+ responce.RespCode.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task Add_Failed()
+ {
+ //arrage
+ string? inTest = null;
+ int? outTest = null;
+
+ _catalogTypeRepository
+ .Setup(s =>
+ s.AddTypeAsync(It.IsAny()))
+ .ReturnsAsync(outTest);
+
+ //act
+ var responce = await _catalogTypeService.AddType(inTest);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.Id.Should().BeNull();
+ responce.ErrorMessage.Should().NotBeNull();
+ responce.RespCode.Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task Delete_succesfull()
+ {
+ //arrage
+ var inTest = 1;
+ var outTest = "test";
+
+ _catalogTypeRepository
+ .Setup(s=>
+ s.DeleteType(It.IsAny()))
+ .ReturnsAsync(outTest);
+
+ //act
+ var responce = await _catalogTypeService.DeleteType(inTest);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.Status.Should().Be(outTest);
+ responce.Status.Should().NotBeNull();
+ responce.ErrorMessage.Should().BeNull();
+ responce.RespCode.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task Delete_failed()
+ {
+ //arrage
+ int? inTest = null;
+ string? outTest = "test";
+
+ _catalogTypeRepository
+ .Setup(s =>
+ s.DeleteType(It.IsAny()))
+ .ReturnsAsync(outTest);
+
+ //act
+ var responce = await _catalogTypeService.DeleteType(inTest);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.Status.Should().BeNull();
+ responce.ErrorMessage.Should().NotBeNull();
+ responce.RespCode.Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task Update_Succesfull()
+ {
+ //arrage
+ var dto = new CatalogTypeDto()
+ {
+ Type = "test"
+ };
+ var entity = new CatalogType()
+ {
+ Type= "test"
+ };
+
+ _mapper.Setup(s => s.Map(It.Is(i => i.Equals(dto)))).Returns(entity);
+
+ _catalogTypeRepository
+ .Setup(s =>
+ s.Update(It.Is(i =>
+ i.Equals(entity))))
+ .ReturnsAsync(entity);
+
+ _mapper
+ .Setup(s =>
+ s.Map(It.Is(i =>
+ i.Equals(entity))))
+ .Returns(dto);
+
+ //act
+ var responce = await _catalogTypeService.UpdateType(dto);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.ErrorMessage.Should().BeNull();
+ responce.RespCode.Should().BeNull();
+ responce.UpdataModel.Should().NotBeNull();
+ responce.UpdataModel.Should().BeSameAs(dto);
+ }
+
+ [Fact]
+ public async Task Update_Failed()
+ {
+ //arrage
+ CatalogTypeDto? dto = null;
+ CatalogType? entity = null;
+
+ _mapper
+ .Setup(s =>
+ s.Map(It.Is(i =>
+ i.Equals(dto))))
+ .Returns(entity);
+
+ _catalogTypeRepository
+ .Setup(s =>
+ s.Update(It.Is(i =>
+ i.Equals(entity))))
+ .ReturnsAsync(entity);
+
+ _mapper
+ .Setup(s =>
+ s.Map(It.Is(i =>
+ i.Equals(entity))))
+ .Returns(dto);
+
+ //act
+ var responce = await _catalogTypeService.UpdateType(dto);
+
+ //assert
+ responce.Should().NotBeNull();
+ responce.ErrorMessage.Should().NotBeNull();
+ responce.RespCode.Should().NotBeNull();
+ responce.UpdataModel.Should().BeNull();
+ responce.UpdataModel.Should().BeSameAs(dto);
+ }
+ }
+}
diff --git a/HomeWork34/Catalog.UnitTest/Services/CatalogitemServiceTest.cs b/HomeWork34/Catalog.UnitTest/Services/CatalogitemServiceTest.cs
new file mode 100644
index 0000000..d63479b
--- /dev/null
+++ b/HomeWork34/Catalog.UnitTest/Services/CatalogitemServiceTest.cs
@@ -0,0 +1,427 @@
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+
+namespace Catalog.UnitTest.Services
+{
+ public class CatalogitemServiceTest
+ {
+ private readonly ICatalogItemService _catalogItemService;
+
+ private readonly Mock _catalogItemRepository;
+ private readonly Mock> _dbContextWrapper;
+ private readonly Mock> _logger;
+ private readonly Mock _mapper;
+
+ private readonly CatalogItem _testItem = new CatalogItem()
+ {
+ Name = "NameTest",
+ Description = "DescriptionTest",
+ Price = 1000,
+ AvailableStock = 100,
+ CatalogBrandId = 1,
+ CatalogTypeId = 1,
+ PictureFileName = "1.pngTest"
+ };
+
+ public CatalogitemServiceTest()
+ {
+ _catalogItemRepository = new Mock();
+ _dbContextWrapper = new Mock>();
+ _logger = new Mock>();
+ _mapper = new Mock();
+
+ var dbContextTransaction = new Mock();
+ _dbContextWrapper.Setup(s => s.BeginTransactionAsync(CancellationToken.None)).ReturnsAsync(dbContextTransaction.Object);
+
+ _catalogItemService = new CatalogItemService(_dbContextWrapper.Object, _logger.Object, _catalogItemRepository.Object, _mapper.Object);
+ }
+
+ [Fact]
+ public async Task AddAsync_Seccusfull()
+ {
+ //arrage
+ var testResult = 1;
+
+ _catalogItemRepository
+ .Setup(s => s.Add(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()))
+ .ReturnsAsync(testResult);
+
+ //act
+ var result = await _catalogItemService
+ .Add(
+ _testItem.Name,
+ _testItem.Description,
+ _testItem.Price,
+ _testItem.AvailableStock,
+ _testItem.CatalogBrandId,
+ _testItem.CatalogTypeId,
+ _testItem.PictureFileName
+ );
+
+ //asert
+ result.Should().NotBeNull();
+ result.Id.Should().Be(testResult);
+ result.RespCode.Should().BeNull();
+ result.ErrorMessage.Should().BeNull();
+ result.Id.Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task AddAsync_Failed()
+ {
+ //arrage
+ int? testResult = null;
+
+ _catalogItemRepository
+ .Setup(s => s.Add(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()))
+ .ReturnsAsync(testResult);
+
+ //act
+ var result = await _catalogItemService
+ .Add(
+ _testItem.Name,
+ _testItem.Description,
+ _testItem.Price,
+ _testItem.AvailableStock,
+ _testItem.CatalogBrandId,
+ _testItem.CatalogTypeId,
+ _testItem.PictureFileName
+ );
+
+ //asert
+ result.Should().NotBeNull();
+ result.RespCode.Should().NotBeNull();
+ result.ErrorMessage.Should().NotBeNull();
+ result.Id.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task GetCatalogItemsByIdAsync_Succesfull()
+ {
+ //arrage
+ int id = 1;
+
+ var catalogItemDtoSuccesfull = new CatalogItemDto()
+ {
+ Name = "Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ PictureUrl = "Test",
+ Id = 1
+ };
+
+ var catalogItemSuccesfull = new CatalogItem()
+ {
+ Name = "Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrandId = 1,
+ CatalogTypeId = 2,
+ Id = 1
+ };
+
+ _catalogItemRepository
+ .Setup(s => s.GetCatalogItemsByIdAsync(It.IsAny()))
+ .ReturnsAsync(catalogItemSuccesfull);
+
+ _mapper.Setup(s => s.Map(
+ It.Is(i => i.Equals(catalogItemSuccesfull))))
+ .Returns(catalogItemDtoSuccesfull);
+
+ //act
+ var responce = await _catalogItemService.GetCatalogItemsByIdAsync(id);
+
+ //asert
+ responce.Should().NotBeNull();
+ responce.Equals(catalogItemDtoSuccesfull);
+ }
+
+ [Fact]
+ public async Task GetCatalogItemsByIdAsync_Failedl()
+ {
+ //arrage
+ int? id = null;
+
+ _catalogItemRepository
+ .Setup(s => s.GetCatalogItemsByIdAsync(It.IsAny()))
+ .Returns((Func)null!);
+
+ //act
+ var responce = await _catalogItemService.GetCatalogItemsByIdAsync(id);
+
+ //asert
+ responce.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task Delete_Seccusfull()
+ {
+ //arrage
+ int id = 1;
+
+ var answer = "text";
+
+ _catalogItemRepository
+ .Setup(s => s.DeleteAsync(It.IsAny()))
+ .ReturnsAsync(answer);
+
+ //act
+ var reponce = await _catalogItemService.DeleteAsync(id);
+
+ //asert
+ reponce.Should().NotBeNull();
+ reponce.Equals(answer);
+ }
+
+ [Fact]
+ public async Task Delete_Failed()
+ {
+ //arrage
+ int? id = null!;
+
+ string? answer = "";
+
+ _catalogItemRepository
+ .Setup(s => s.DeleteAsync(It.IsAny()))
+ .ReturnsAsync(answer);
+
+ //act
+ var reponce = await _catalogItemService.DeleteAsync(id);
+
+ //asert
+ reponce.Should().NotBeNull();
+ reponce.Equals(answer);
+ }
+
+ [Fact]
+ public async Task Update_Succesfull()
+ {
+ //arrage
+ var catalogItemDtoSuccesfull = new CatalogItemDto()
+ {
+ Name = "Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ PictureUrl = "Test",
+ Id = 1
+ };
+
+ var catalogItemSuccesfull = new CatalogItem()
+ {
+ Name = "Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrandId = 1,
+ CatalogTypeId = 2,
+ Id = 1
+ };
+
+ _mapper
+ .Setup(s =>
+ s.Map(It.Is(i =>
+ i.Equals(catalogItemDtoSuccesfull))))
+ .Returns(catalogItemSuccesfull);
+
+ _catalogItemRepository
+ .Setup(s => s.Update(It.IsAny()))
+ .ReturnsAsync(catalogItemSuccesfull);
+
+
+ _mapper
+ .Setup(s =>
+ s.Map(It.Is(i =>
+ i.Equals(catalogItemSuccesfull))))
+ .Returns(catalogItemDtoSuccesfull);
+
+ //act
+ var reponce = await _catalogItemService
+ .UpdateAsync(catalogItemDtoSuccesfull);
+
+ //asert
+ reponce.Should().NotBeNull();
+ reponce.Description.Should().Be("Test");
+ }
+
+ [Fact]
+ public async Task Update_Failed()
+ {
+ //arrage
+
+ CatalogItemDto catalog = null;
+
+ _catalogItemRepository
+ .Setup(s => s.Update(It.IsAny()))
+ .Returns((Func)null!);
+
+ //act
+ var reponce = await _catalogItemService.UpdateAsync(catalog);
+
+ //asert
+ reponce.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task GetCatalogItemByTypeAsync_Succesfull()
+ {
+ //arrage
+ var id = 1;
+ var list = new List()
+ {
+ new CatalogItem()
+ {
+ Name ="Test",
+ },
+ new CatalogItem()
+ {
+ Name ="Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrandId = 1,
+ CatalogTypeId = 2,
+ PictureFileName = "Test"
+ },
+ new CatalogItem()
+ {
+ Name ="Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrandId = 1,
+ CatalogTypeId = 2,
+ PictureFileName = "Test"
+ },
+ };
+
+ var dto = new CatalogItemDto()
+ {
+ Name = "Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrand = new CatalogBrandDto() { Brand = "test" },
+ CatalogType = new CatalogTypeDto() { Type = "test" },
+
+ };
+
+ _mapper.Setup(s => s.Map(It.IsAny())).Returns(dto);
+
+ _catalogItemRepository
+ .Setup(s => s.GetCatalogItemsByTypeAsync(It.IsAny()))
+ .ReturnsAsync(list);
+
+ //act
+ var reponce = await _catalogItemService.GetCatalogItemByTypeAsync(id);
+
+ //asert
+ reponce.Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task GetCatalogItemByTypeAsync_Failed()
+ {
+ //arrage
+ int? id = null;
+ List? list = null;
+
+ CatalogItemDto? dto = null;
+
+ _mapper.Setup(s => s.Map(It.IsAny())).Returns(dto!);
+
+ _catalogItemRepository
+ .Setup(s => s.GetCatalogItemsByTypeAsync(It.IsAny()))
+ .ReturnsAsync(list!);
+
+ //act
+ var reponce = await _catalogItemService.GetCatalogItemByTypeAsync(id);
+
+ //asert
+ reponce.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task GetCatalogItemByandAsync_Succesfull()
+ {
+ //arrage
+ var id = 1;
+ var list = new List()
+ {
+ new CatalogItem()
+ {
+ Name ="Test",
+ },
+ new CatalogItem()
+ {
+ Name ="Test",
+ },
+ new CatalogItem()
+ {
+ Name ="Test",
+ },
+ };
+
+ var dto = new CatalogItemDto()
+ {
+ Name = "Test",
+ AvailableStock = 5,
+ Description = "Test",
+ Price = 10,
+ CatalogBrand = new CatalogBrandDto() { Brand = "test" },
+ CatalogType = new CatalogTypeDto() { Type = "test" },
+
+ };
+
+ _mapper.Setup(s => s.Map(It.IsAny())).Returns(dto);
+
+ _catalogItemRepository
+ .Setup(s => s.GetCatalogItemsByBrandAsync(It.IsAny()))
+ .ReturnsAsync(list);
+
+ //act
+ var reponce = await _catalogItemService.GetCatalogItemByBrandAsync(id);
+
+ //asert
+ reponce.Should().NotBeNull();
+ }
+ [Fact]
+ public async Task GetCatalogItemByBrandAsync_Failed()
+ {
+ //arrage
+ int? id = null;
+ List? list = null;
+
+ CatalogItemDto? dto = null;
+
+ _mapper.Setup(s => s.Map(It.IsAny())).Returns(dto!);
+
+ _catalogItemRepository
+ .Setup(s => s.GetCatalogItemsByBrandAsync(It.IsAny()))
+ .ReturnsAsync(list!);
+
+ //act
+ var reponce = await _catalogItemService.GetCatalogItemByBrandAsync(id);
+
+ //asert
+ reponce.Should().BeNull();
+ }
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/.dockerignore b/HomeWork34/Catalog/Catalog.Host/.dockerignore
new file mode 100644
index 0000000..cd967fc
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/.dockerignore
@@ -0,0 +1,25 @@
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/.idea
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Catalog.Host.csproj b/HomeWork34/Catalog/Catalog.Host/Catalog.Host.csproj
new file mode 100644
index 0000000..2847e97
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Catalog.Host.csproj
@@ -0,0 +1,47 @@
+
+
+
+ net8.0
+ enable
+ enable
+ Linux
+ ../../settings.ruleset
+ true
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/HomeWork34/Catalog/Catalog.Host/Catalog.Host.csproj.user b/HomeWork34/Catalog/Catalog.Host/Catalog.Host.csproj.user
new file mode 100644
index 0000000..46b1b35
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Catalog.Host.csproj.user
@@ -0,0 +1,12 @@
+
+
+
+ ProjectDebugger
+
+
+ Catalog.Host
+
+
+ ProjectDebugger
+
+
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Configurations/CatalogConfig.cs b/HomeWork34/Catalog/Catalog.Host/Configurations/CatalogConfig.cs
new file mode 100644
index 0000000..a86b76e
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Configurations/CatalogConfig.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Configurations;
+
+public class CatalogConfig
+{
+ public string CdnHost { get; set; }
+ public string ImgUrl { get; set; }
+ public string ConnectionString { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogBffController.cs b/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogBffController.cs
new file mode 100644
index 0000000..1a25ab6
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogBffController.cs
@@ -0,0 +1,118 @@
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Models.enums;
+using Catalog.Host.Models.Requests;
+using Catalog.Host.Models.Response;
+using Catalog.Host.Services.Interfaces;
+
+namespace Catalog.Host.Controllers;
+
+[ApiController]
+[Route(ComponentDefaults.DefaultRoute)]
+public class CatalogBffController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly ICatalogService _catalogService;
+ private readonly ICatalogItemService _catalogItemService;
+ private readonly ICatalogTypeService _catalogTypeService;
+ private readonly ICatalogBrandService _catalogBrandService;
+ public CatalogBffController(
+ ILogger logger,
+ ICatalogService catalogService,
+ ICatalogItemService catalogItemService,
+ ICatalogBrandService catalogBrandService,
+ ICatalogTypeService catalogTypeService
+ )
+ {
+ _logger = logger;
+ _catalogService = catalogService;
+ _catalogItemService = catalogItemService;
+ _catalogBrandService = catalogBrandService;
+ _catalogTypeService = catalogTypeService;
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(PaginatedItemsResponse), (int)HttpStatusCode.OK)]
+ public async Task Items(PaginatedItemsRequest request)
+ {
+ if (request is null)
+ {
+ _logger.LogWarning("request null!");
+ var responce = new BaseResponce()
+ {
+ ErrorMessage = "request null!"
+ };
+ responce.GetResponce();
+
+ return Ok(responce);
+ }
+ var result = await _catalogService.GetByPageAsync(request.PageSize, request.PageIndex, request.Filters);
+ return Ok(result);
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(CatalogItemDto), (int)HttpStatusCode.OK)]
+ public async Task GetById(int? id)
+ {
+ if(id is null)
+ {
+ var responce = new BaseResponce()
+ {
+ ErrorMessage = "id is null"
+ };
+ responce.GetResponce();
+ return Ok(responce);
+ }
+ var result = await _catalogItemService.GetCatalogItemsByIdAsync(id);
+ return Ok(result);
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(PaginatedItemsResponse), (int)HttpStatusCode.OK)]
+ public async Task GetByBrand(int? idBrand)
+ {
+ if (idBrand is null)
+ {
+ var responce = new BaseResponce()
+ {
+ ErrorMessage = "id is null"
+ };
+ responce.GetResponce();
+ return Ok(responce);
+ }
+ var result = await _catalogItemService.GetCatalogItemByBrandAsync(idBrand);
+ return Ok(result);
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(ListResponse), (int)HttpStatusCode.OK)]
+ public async Task GetByType(int? idType)
+ {
+ if (idType is null)
+ {
+ var responce = new BaseResponce()
+ {
+ ErrorMessage = "id is null"
+ };
+ responce.GetResponce();
+ return Ok(responce);
+ }
+ var result = await _catalogItemService.GetCatalogItemByTypeAsync(idType);
+ return Ok(result);
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(ListResponse), (int)HttpStatusCode.OK)]
+ public async Task GetListBrand()
+ {
+ var result = await _catalogBrandService.GetList();
+ return Ok(result);
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(ListResponse), (int)HttpStatusCode.OK)]
+ public async Task GetListType()
+ {
+ var result = await _catalogTypeService.GetList();
+ return Ok(result);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogBrandController.cs b/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogBrandController.cs
new file mode 100644
index 0000000..b192ed1
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogBrandController.cs
@@ -0,0 +1,37 @@
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Models.Response;
+using Catalog.Host.Services.Interfaces;
+
+namespace Catalog.Host.Controllers;
+
+[ApiController]
+[Route(ComponentDefaults.DefaultRoute)]
+public class CatalogBrandController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly ICatalogBrandService _service;
+
+ public CatalogBrandController(ILogger logger, ICatalogBrandService catalogBrandService)
+ {
+ _logger = logger;
+ _service = catalogBrandService;
+ }
+
+ [HttpPost]
+ public async Task AddBrand(string? brand)
+ {
+ return await _service.AddAsync(brand);
+ }
+
+ [HttpPut]
+ public async Task> UpdateBrand(CatalogBrandDto? catalogBrand)
+ {
+ return await _service.UpdateAsync(catalogBrand);
+ }
+
+ [HttpDelete]
+ public async Task DeleteBrand(int id)
+ {
+ return await _service.DeleteAsync(id);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogItemController.cs b/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogItemController.cs
new file mode 100644
index 0000000..2bbb8da
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogItemController.cs
@@ -0,0 +1,59 @@
+using System.Net;
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Models.Requests;
+using Catalog.Host.Models.Response;
+using Catalog.Host.Services.Interfaces;
+using Infrastructure;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Catalog.Host.Controllers;
+
+[ApiController]
+[Route(ComponentDefaults.DefaultRoute)]
+public class CatalogItemController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly ICatalogItemService _catalogItemService;
+
+ public CatalogItemController(
+ ILogger logger,
+ ICatalogItemService catalogItemService)
+ {
+ _logger = logger;
+ _catalogItemService = catalogItemService;
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(AddItemResponse), (int)HttpStatusCode.OK)]
+ public async Task Add(CreateProductRequest request)
+ {
+ var result = await _catalogItemService.Add(
+ request.Name,
+ request.Description,
+ request.Price,
+ request.AvailableStock,
+ request.CatalogBrandId,
+ request.CatalogTypeId,
+ request.PictureFileName);
+
+ return Ok(result);
+ }
+
+ [HttpGet]
+ public async Task GetById(int id)
+ {
+ return await _catalogItemService.GetCatalogItemsByIdAsync(id);
+ }
+
+ [HttpDelete]
+ public async Task Delete(int id)
+ {
+ return await _catalogItemService.DeleteAsync(id);
+ }
+
+ [HttpPut]
+ public async Task Update(CatalogItemDto catalogItemDto)
+ {
+ return await _catalogItemService.UpdateAsync(catalogItemDto);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogTypeController.cs b/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogTypeController.cs
new file mode 100644
index 0000000..243f41e
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Controllers/CatalogTypeController.cs
@@ -0,0 +1,66 @@
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Models.Response;
+using Catalog.Host.Services.Interfaces;
+
+namespace Catalog.Host.Controllers;
+
+[ApiController]
+[Route(ComponentDefaults.DefaultRoute)]
+public class CatalogTypeController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly ICatalogTypeService _serivice;
+
+ public CatalogTypeController(
+ ILogger logger,
+ ICatalogTypeService service)
+ {
+ _logger = logger;
+ _serivice = service;
+ }
+
+ [HttpPost]
+ public async Task AddType(string? type)
+ {
+ if(type is null)
+ {
+ return new IdResponse()
+ {
+ ErrorMessage = "Null data",
+ RespCode = ResponceCode.Failed
+ };
+ }
+
+ return await _serivice.AddType(type);
+ }
+
+ [HttpPut]
+ public async Task> UpdateType(CatalogTypeDto? catalogType)
+ {
+ if (catalogType is null)
+ {
+ return new UpdataResponse()
+ {
+ ErrorMessage = "Null data",
+ RespCode = ResponceCode.Error
+ };
+ }
+
+ return await _serivice.UpdateType(catalogType);
+ }
+
+ [HttpDelete]
+ public async Task DeleteType(int? id)
+ {
+ if (id is null)
+ {
+ return new DeleteResponse()
+ {
+ RespCode = ResponceCode.Failed,
+ ErrorMessage = "id is null"
+ };
+ }
+
+ return await _serivice.DeleteType(id);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/ApplicationDbContext.cs b/HomeWork34/Catalog/Catalog.Host/Data/ApplicationDbContext.cs
new file mode 100644
index 0000000..0e3750f
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/ApplicationDbContext.cs
@@ -0,0 +1,25 @@
+#pragma warning disable CS8618
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Data.EntityConfigurations;
+using Microsoft.EntityFrameworkCore;
+
+namespace Catalog.Host.Data;
+
+public class ApplicationDbContext : DbContext
+{
+ public ApplicationDbContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ public DbSet CatalogItems { get; set; }
+ public DbSet CatalogBrands { get; set; }
+ public DbSet CatalogTypes { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ builder.ApplyConfiguration(new CatalogBrandEntityTypeConfiguration());
+ builder.ApplyConfiguration(new CatalogTypeEntityTypeConfiguration());
+ builder.ApplyConfiguration(new CatalogItemEntityTypeConfiguration());
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/DbInitializer.cs b/HomeWork34/Catalog/Catalog.Host/Data/DbInitializer.cs
new file mode 100644
index 0000000..5f63250
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/DbInitializer.cs
@@ -0,0 +1,74 @@
+using Catalog.Host.Data.Entities;
+
+namespace Catalog.Host.Data;
+
+public static class DbInitializer
+{
+ public static async Task Initialize(ApplicationDbContext context)
+ {
+ await context.Database.EnsureCreatedAsync();
+
+ if (!context.CatalogBrands.Any())
+ {
+ await context.CatalogBrands.AddRangeAsync(GetPreconfiguredCatalogBrands());
+
+ await context.SaveChangesAsync();
+ }
+
+ if (!context.CatalogTypes.Any())
+ {
+ await context.CatalogTypes.AddRangeAsync(GetPreconfiguredCatalogTypes());
+
+ await context.SaveChangesAsync();
+ }
+
+ if (!context.CatalogItems.Any())
+ {
+ await context.CatalogItems.AddRangeAsync(GetPreconfiguredItems());
+
+ await context.SaveChangesAsync();
+ }
+ }
+
+ private static IEnumerable GetPreconfiguredCatalogBrands()
+ {
+ return new List()
+ {
+ new CatalogBrand() { Brand = "Azure" },
+ new CatalogBrand() { Brand = ".NET" },
+ new CatalogBrand() { Brand = "Visual Studio" },
+ new CatalogBrand() { Brand = "SQL Server" },
+ new CatalogBrand() { Brand = "Other" }
+ };
+ }
+
+ private static IEnumerable GetPreconfiguredCatalogTypes()
+ {
+ return new List()
+ {
+ new CatalogType() { Type = "Mug" },
+ new CatalogType() { Type = "T-Shirt" },
+ new CatalogType() { Type = "Sheet" },
+ new CatalogType() { Type = "USB Memory Stick" }
+ };
+ }
+
+ private static IEnumerable GetPreconfiguredItems()
+ {
+ return new List()
+ {
+ new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureFileName = "1.png" },
+ new CatalogItem { CatalogTypeId = 1, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price = 8.50M, PictureFileName = "2.png" },
+ new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureFileName = "3.png" },
+ new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureFileName = "4.png" },
+ new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 5, AvailableStock = 100, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureFileName = "5.png" },
+ new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureFileName = "6.png" },
+ new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureFileName = "7.png" },
+ new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureFileName = "8.png" },
+ new CatalogItem { CatalogTypeId = 1, CatalogBrandId = 5, AvailableStock = 100, Description = "Cup White Mug", Name = "Cup White Mug", Price = 12, PictureFileName = "9.png" },
+ new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureFileName = "10.png" },
+ new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 2, AvailableStock = 100, Description = "Cup Sheet", Name = "Cup Sheet", Price = 8.5M, PictureFileName = "11.png" },
+ new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureFileName = "12.png" },
+ };
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogBrand.cs b/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogBrand.cs
new file mode 100644
index 0000000..9f63c1f
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogBrand.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Data.Entities;
+
+public class CatalogBrand
+{
+ public int Id { get; set; }
+
+ public string Brand { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogItem.cs b/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogItem.cs
new file mode 100644
index 0000000..959f5c0
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogItem.cs
@@ -0,0 +1,26 @@
+#pragma warning disable CS8618
+
+namespace Catalog.Host.Data.Entities;
+
+public class CatalogItem
+{
+ public int Id { get; set; }
+
+ public string Name { get; set; }
+
+ public string Description { get; set; }
+
+ public decimal Price { get; set; }
+
+ public string PictureFileName { get; set; }
+
+ public int CatalogTypeId { get; set; }
+
+ public CatalogType CatalogType { get; set; }
+
+ public int CatalogBrandId { get; set; }
+
+ public CatalogBrand CatalogBrand { get; set; }
+
+ public int AvailableStock { get; set; }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogType.cs b/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogType.cs
new file mode 100644
index 0000000..5eaddda
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/Entities/CatalogType.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Data.Entities;
+
+public class CatalogType
+{
+ public int Id { get; set; }
+
+ public string Type { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/Entities/ErrorResponce.cs b/HomeWork34/Catalog/Catalog.Host/Data/Entities/ErrorResponce.cs
new file mode 100644
index 0000000..8b7f179
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/Entities/ErrorResponce.cs
@@ -0,0 +1,6 @@
+namespace Catalog.Host.Data.Entities
+{
+ public class ErrorResponce
+ {
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs b/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs
new file mode 100644
index 0000000..2ea4be3
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs
@@ -0,0 +1,24 @@
+using Catalog.Host.Data.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Catalog.Host.Data.EntityConfigurations;
+
+public class CatalogBrandEntityTypeConfiguration
+ : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("CatalogBrand");
+
+ builder.HasKey(ci => ci.Id);
+
+ builder.Property(ci => ci.Id)
+ .UseHiLo("catalog_brand_hilo")
+ .IsRequired();
+
+ builder.Property(cb => cb.Brand)
+ .IsRequired()
+ .HasMaxLength(100);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs b/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs
new file mode 100644
index 0000000..0fc6e91
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs
@@ -0,0 +1,36 @@
+using Catalog.Host.Data.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Catalog.Host.Data.EntityConfigurations;
+
+public class CatalogItemEntityTypeConfiguration
+ : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("Catalog");
+
+ builder.Property(ci => ci.Id)
+ .UseHiLo("catalog_hilo")
+ .IsRequired();
+
+ builder.Property(ci => ci.Name)
+ .IsRequired(true)
+ .HasMaxLength(50);
+
+ builder.Property(ci => ci.Price)
+ .IsRequired(true);
+
+ builder.Property(ci => ci.PictureFileName)
+ .IsRequired(false);
+
+ builder.HasOne(ci => ci.CatalogBrand)
+ .WithMany()
+ .HasForeignKey(ci => ci.CatalogBrandId);
+
+ builder.HasOne(ci => ci.CatalogType)
+ .WithMany()
+ .HasForeignKey(ci => ci.CatalogTypeId);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs b/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs
new file mode 100644
index 0000000..1b92189
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs
@@ -0,0 +1,25 @@
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Catalog.Host.Data.EntityConfigurations;
+
+public class CatalogTypeEntityTypeConfiguration
+ : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("CatalogType");
+
+ builder.HasKey(ci => ci.Id);
+
+ builder.Property(ci => ci.Id)
+ .UseHiLo("catalog_type_hilo")
+ .IsRequired();
+
+ builder.Property(cb => cb.Type)
+ .IsRequired()
+ .HasMaxLength(100);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Data/PaginatedItems.cs b/HomeWork34/Catalog/Catalog.Host/Data/PaginatedItems.cs
new file mode 100644
index 0000000..130e77c
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Data/PaginatedItems.cs
@@ -0,0 +1,8 @@
+namespace Catalog.Host.Data;
+
+public class PaginatedItems
+{
+ public long TotalCount { get; init; }
+
+ public IEnumerable Data { get; init; } = null!;
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Dockerfile b/HomeWork34/Catalog/Catalog.Host/Dockerfile
new file mode 100644
index 0000000..9e09d15
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Dockerfile
@@ -0,0 +1,14 @@
+# https://hub.docker.com/_/microsoft-dotnet
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+COPY . /src
+WORKDIR /src/Catalog/Catalog.Host
+RUN dotnet publish -c Release -o /app
+
+
+# final stage/image
+FROM mcr.microsoft.com/dotnet/aspnet:8.0
+WORKDIR /app
+COPY --from=build /app ./
+ENTRYPOINT ["dotnet", "Catalog.Host.dll"]
+
+
diff --git a/HomeWork34/Catalog/Catalog.Host/GlobalUsing.cs b/HomeWork34/Catalog/Catalog.Host/GlobalUsing.cs
new file mode 100644
index 0000000..e1256a5
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/GlobalUsing.cs
@@ -0,0 +1,12 @@
+global using Infrastructure.Exeptions;
+global using System.Net;
+global using Infrastructure.Enums;
+global using Infrastructure.Models;
+global using Microsoft.EntityFrameworkCore;
+global using Infrastructure.Services.Interfaces;
+global using Infrastructure.Services;
+global using Infrastructure;
+global using Microsoft.AspNetCore.Mvc;
+global using Microsoft.EntityFrameworkCore.Metadata.Builders;
+global using AutoMapper;
+global using Microsoft.Extensions.Options;
diff --git a/HomeWork34/Catalog/Catalog.Host/Mapping/CatalogItemPictureResolver.cs b/HomeWork34/Catalog/Catalog.Host/Mapping/CatalogItemPictureResolver.cs
new file mode 100644
index 0000000..7d1cc78
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Mapping/CatalogItemPictureResolver.cs
@@ -0,0 +1,27 @@
+using AutoMapper;
+using Catalog.Host.Configurations;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Microsoft.Extensions.Options;
+
+namespace Catalog.Host.Mapping;
+
+public class CatalogItemPictureResolver : IMemberValueResolver
+{
+ private readonly CatalogConfig _config;
+
+ public CatalogItemPictureResolver(IOptionsSnapshot config)
+ {
+ _config = config.Value;
+ }
+
+ public object Resolve(
+ CatalogItem source,
+ CatalogItemDto destination,
+ string sourceMember,
+ object destMember,
+ ResolutionContext context)
+ {
+ return $"{_config.CdnHost}/{_config.ImgUrl}/{sourceMember}";
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Mapping/MappingProfile.cs b/HomeWork34/Catalog/Catalog.Host/Mapping/MappingProfile.cs
new file mode 100644
index 0000000..0adc55b
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Mapping/MappingProfile.cs
@@ -0,0 +1,20 @@
+using AutoMapper;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+
+namespace Catalog.Host.Mapping;
+
+public class MappingProfile : Profile
+{
+ public MappingProfile()
+ {
+ CreateMap()
+ .ForMember("PictureUrl", opt
+ => opt.MapFrom(c => c.PictureFileName))
+ .ReverseMap();
+
+ CreateMap().ReverseMap();
+ CreateMap().ReverseMap();
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.Designer.cs b/HomeWork34/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.Designer.cs
new file mode 100644
index 0000000..bcd1d1f
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.Designer.cs
@@ -0,0 +1,133 @@
+//
+using Catalog.Host.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Catalog.Host.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20220108225624_InitialMigration")]
+ partial class InitialMigration
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "6.0.1")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.HasSequence("catalog_brand_hilo")
+ .IncrementsBy(10);
+
+ modelBuilder.HasSequence("catalog_hilo")
+ .IncrementsBy(10);
+
+ modelBuilder.HasSequence("catalog_type_hilo")
+ .IncrementsBy(10);
+
+ modelBuilder.Entity("Catalog.Host.Data.Entities.CatalogItem", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseHiLo(b.Property("Id"), "catalog_hilo");
+
+ b.Property("AvailableStock")
+ .HasColumnType("integer");
+
+ b.Property("CatalogBrandId")
+ .HasColumnType("integer");
+
+ b.Property("CatalogTypeId")
+ .HasColumnType("integer");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("PictureFileName")
+ .HasColumnType("text");
+
+ b.Property("Price")
+ .HasColumnType("numeric");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CatalogBrandId");
+
+ b.HasIndex("CatalogTypeId");
+
+ b.ToTable("Catalog", (string)null);
+ });
+
+ modelBuilder.Entity("Catalog.Host.Data.Enums.CatalogBrand", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseHiLo(b.Property("Id"), "catalog_brand_hilo");
+
+ b.Property("Brand")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.HasKey("Id");
+
+ b.ToTable("CatalogBrand", (string)null);
+ });
+
+ modelBuilder.Entity("Catalog.Host.Data.Enums.CatalogType", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseHiLo(b.Property("Id"), "catalog_type_hilo");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.HasKey("Id");
+
+ b.ToTable("CatalogType", (string)null);
+ });
+
+ modelBuilder.Entity("Catalog.Host.Data.Entities.CatalogItem", b =>
+ {
+ b.HasOne("Catalog.Host.Data.Enums.CatalogBrand", "CatalogBrand")
+ .WithMany()
+ .HasForeignKey("CatalogBrandId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Catalog.Host.Data.Enums.CatalogType", "CatalogType")
+ .WithMany()
+ .HasForeignKey("CatalogTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("CatalogBrand");
+
+ b.Navigation("CatalogType");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.cs b/HomeWork34/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.cs
new file mode 100644
index 0000000..df8f05e
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.cs
@@ -0,0 +1,109 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Catalog.Host.Migrations
+{
+ public partial class InitialMigration : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateSequence(
+ name: "catalog_brand_hilo",
+ incrementBy: 10);
+
+ migrationBuilder.CreateSequence(
+ name: "catalog_hilo",
+ incrementBy: 10);
+
+ migrationBuilder.CreateSequence(
+ name: "catalog_type_hilo",
+ incrementBy: 10);
+
+ migrationBuilder.CreateTable(
+ name: "CatalogBrand",
+ columns: table => new
+ {
+ Id = table.Column(type: "integer", nullable: false),
+ Brand = table.Column(type: "character varying(100)", maxLength: 100, nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_CatalogBrand", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "CatalogType",
+ columns: table => new
+ {
+ Id = table.Column(type: "integer", nullable: false),
+ Type = table.Column(type: "character varying(100)", maxLength: 100, nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_CatalogType", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Catalog",
+ columns: table => new
+ {
+ Id = table.Column(type: "integer", nullable: false),
+ Name = table.Column(type: "character varying(50)", maxLength: 50, nullable: false),
+ Description = table.Column(type: "text", nullable: false),
+ Price = table.Column(type: "numeric", nullable: false),
+ PictureFileName = table.Column(type: "text", nullable: true),
+ CatalogTypeId = table.Column(type: "integer", nullable: false),
+ CatalogBrandId = table.Column(type: "integer", nullable: false),
+ AvailableStock = table.Column(type: "integer", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Catalog", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Catalog_CatalogBrand_CatalogBrandId",
+ column: x => x.CatalogBrandId,
+ principalTable: "CatalogBrand",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_Catalog_CatalogType_CatalogTypeId",
+ column: x => x.CatalogTypeId,
+ principalTable: "CatalogType",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Catalog_CatalogBrandId",
+ table: "Catalog",
+ column: "CatalogBrandId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Catalog_CatalogTypeId",
+ table: "Catalog",
+ column: "CatalogTypeId");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Catalog");
+
+ migrationBuilder.DropTable(
+ name: "CatalogBrand");
+
+ migrationBuilder.DropTable(
+ name: "CatalogType");
+
+ migrationBuilder.DropSequence(
+ name: "catalog_brand_hilo");
+
+ migrationBuilder.DropSequence(
+ name: "catalog_hilo");
+
+ migrationBuilder.DropSequence(
+ name: "catalog_type_hilo");
+ }
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Migrations/ApplicationDbContextModelSnapshot.cs b/HomeWork34/Catalog/Catalog.Host/Migrations/ApplicationDbContextModelSnapshot.cs
new file mode 100644
index 0000000..7298040
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -0,0 +1,131 @@
+//
+using Catalog.Host.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Catalog.Host.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ partial class ApplicationDbContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "6.0.1")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.HasSequence("catalog_brand_hilo")
+ .IncrementsBy(10);
+
+ modelBuilder.HasSequence("catalog_hilo")
+ .IncrementsBy(10);
+
+ modelBuilder.HasSequence("catalog_type_hilo")
+ .IncrementsBy(10);
+
+ modelBuilder.Entity("Catalog.Host.Data.Entities.CatalogItem", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseHiLo(b.Property("Id"), "catalog_hilo");
+
+ b.Property("AvailableStock")
+ .HasColumnType("integer");
+
+ b.Property("CatalogBrandId")
+ .HasColumnType("integer");
+
+ b.Property("CatalogTypeId")
+ .HasColumnType("integer");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("PictureFileName")
+ .HasColumnType("text");
+
+ b.Property("Price")
+ .HasColumnType("numeric");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CatalogBrandId");
+
+ b.HasIndex("CatalogTypeId");
+
+ b.ToTable("Catalog", (string)null);
+ });
+
+ modelBuilder.Entity("Catalog.Host.Data.Enums.CatalogBrand", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseHiLo(b.Property("Id"), "catalog_brand_hilo");
+
+ b.Property("Brand")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.HasKey("Id");
+
+ b.ToTable("CatalogBrand", (string)null);
+ });
+
+ modelBuilder.Entity("Catalog.Host.Data.Enums.CatalogType", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseHiLo(b.Property("Id"), "catalog_type_hilo");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.HasKey("Id");
+
+ b.ToTable("CatalogType", (string)null);
+ });
+
+ modelBuilder.Entity("Catalog.Host.Data.Entities.CatalogItem", b =>
+ {
+ b.HasOne("Catalog.Host.Data.Enums.CatalogBrand", "CatalogBrand")
+ .WithMany()
+ .HasForeignKey("CatalogBrandId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Catalog.Host.Data.Enums.CatalogType", "CatalogType")
+ .WithMany()
+ .HasForeignKey("CatalogTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("CatalogBrand");
+
+ b.Navigation("CatalogType");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogBrandDto.cs b/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogBrandDto.cs
new file mode 100644
index 0000000..cb2d35e
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogBrandDto.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Models.Dtos;
+
+public class CatalogBrandDto : BaseResponce
+{
+ public int Id { get; set; }
+
+ public string Brand { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogItemDto.cs b/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogItemDto.cs
new file mode 100644
index 0000000..9dbdf44
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogItemDto.cs
@@ -0,0 +1,21 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Models.Dtos;
+
+public class CatalogItemDto : BaseResponce
+{
+ public int Id { get; set; }
+
+ public string Name { get; set; }
+
+ public string Description { get; set; }
+
+ public decimal Price { get; set; }
+
+ public string PictureUrl { get; set; }
+
+ public CatalogTypeDto CatalogType { get; set; }
+
+ public CatalogBrandDto CatalogBrand { get; set; }
+
+ public int AvailableStock { get; set; }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogTypeDto.cs b/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogTypeDto.cs
new file mode 100644
index 0000000..595e727
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Dtos/CatalogTypeDto.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Models.Dtos;
+
+public class CatalogTypeDto : BaseResponce
+{
+ public int Id { get; set; }
+
+ public string Type { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Requests/CreateProductRequest.cs b/HomeWork34/Catalog/Catalog.Host/Models/Requests/CreateProductRequest.cs
new file mode 100644
index 0000000..20de15b
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Requests/CreateProductRequest.cs
@@ -0,0 +1,20 @@
+using Catalog.Host.Data.Entities;
+
+namespace Catalog.Host.Models.Requests;
+
+public class CreateProductRequest
+{
+ public string Name { get; set; } = null!;
+
+ public string Description { get; set; } = null!;
+
+ public decimal Price { get; set; }
+
+ public string PictureFileName { get; set; } = null!;
+
+ public int CatalogTypeId { get; set; }
+
+ public int CatalogBrandId { get; set; }
+
+ public int AvailableStock { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Requests/PaginatedItemsRequest.cs b/HomeWork34/Catalog/Catalog.Host/Models/Requests/PaginatedItemsRequest.cs
new file mode 100644
index 0000000..44e18aa
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Requests/PaginatedItemsRequest.cs
@@ -0,0 +1,11 @@
+namespace Catalog.Host.Models.Requests;
+
+public class PaginatedItemsRequest
+ where T : notnull
+{
+ public int PageIndex { get; set; }
+
+ public int PageSize { get; set; }
+
+ public Dictionary? Filters { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Response/AddItemResponse.cs b/HomeWork34/Catalog/Catalog.Host/Models/Response/AddItemResponse.cs
new file mode 100644
index 0000000..2c55f7a
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Response/AddItemResponse.cs
@@ -0,0 +1,6 @@
+namespace Catalog.Host.Models.Response;
+
+public class AddItemResponse : BaseResponce
+{
+ public T? Id { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Response/DeleteResponse.cs b/HomeWork34/Catalog/Catalog.Host/Models/Response/DeleteResponse.cs
new file mode 100644
index 0000000..574a285
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Response/DeleteResponse.cs
@@ -0,0 +1,13 @@
+namespace Catalog.Host.Models.Response
+{
+ public class DeleteResponse : BaseResponce
+ {
+ public string? Status { get; set; }
+
+ public override ResponceCode GetResponce() =>
+ Status is null ?
+ ResponceCode.Null :
+ ResponceCode.Seccusfull;
+
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Response/IdResponse.cs b/HomeWork34/Catalog/Catalog.Host/Models/Response/IdResponse.cs
new file mode 100644
index 0000000..0699cfe
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Response/IdResponse.cs
@@ -0,0 +1,11 @@
+namespace Catalog.Host.Models.Response
+{
+ public class IdResponse : BaseResponce
+ {
+ public int? Id { get; set; }
+ public override ResponceCode GetResponce() =>
+ Id is null ?
+ ResponceCode.Null :
+ ResponceCode.Seccusfull;
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Response/ListResponse.cs b/HomeWork34/Catalog/Catalog.Host/Models/Response/ListResponse.cs
new file mode 100644
index 0000000..a1bce61
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Response/ListResponse.cs
@@ -0,0 +1,8 @@
+namespace Catalog.Host.Models.Response
+{
+ public class ListResponse : BaseResponce
+ {
+ public IEnumerable? List { get; set; }
+ public override ResponceCode GetResponce() => List is null ? ResponceCode.Null : ResponceCode.Seccusfull;
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Response/PaginatedItemsResponse.cs b/HomeWork34/Catalog/Catalog.Host/Models/Response/PaginatedItemsResponse.cs
new file mode 100644
index 0000000..48d3e66
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Response/PaginatedItemsResponse.cs
@@ -0,0 +1,16 @@
+
+namespace Catalog.Host.Models.Response;
+
+public class PaginatedItemsResponse : BaseResponce
+{
+ public int PageIndex { get; init; }
+
+ public int PageSize { get; init; }
+
+ public long Count { get; init; }
+
+ public IEnumerable? Data { get; init; }
+
+ public override ResponceCode GetResponce() => Data is null ? ResponceCode.Null : ResponceCode.Seccusfull;
+
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/Response/UpdataResponse.cs b/HomeWork34/Catalog/Catalog.Host/Models/Response/UpdataResponse.cs
new file mode 100644
index 0000000..8114df1
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/Response/UpdataResponse.cs
@@ -0,0 +1,12 @@
+namespace Catalog.Host.Models.Response
+{
+ public class UpdataResponse : BaseResponce
+ {
+ public T? UpdataModel { get; set; }
+
+ public override ResponceCode GetResponce() =>
+ UpdataModel is null ?
+ ResponceCode.Null :
+ ResponceCode.Seccusfull;
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Models/enums/CatalogTypeFilter.cs b/HomeWork34/Catalog/Catalog.Host/Models/enums/CatalogTypeFilter.cs
new file mode 100644
index 0000000..be41d52
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Models/enums/CatalogTypeFilter.cs
@@ -0,0 +1,8 @@
+namespace Catalog.Host.Models.enums
+{
+ public enum CatalogTypeFilter
+ {
+ Brand,
+ Type
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Program.cs b/HomeWork34/Catalog/Catalog.Host/Program.cs
new file mode 100644
index 0000000..3291aae
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Program.cs
@@ -0,0 +1,86 @@
+using Catalog.Host.Configurations;
+using Catalog.Host.Data;
+using Catalog.Host.Repositories;
+using Catalog.Host.Repositories.Interfaces;
+using Catalog.Host.Services;
+using Catalog.Host.Services.Interfaces;
+
+var configuration = GetConfiguration();
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddControllers();
+builder.Services.Configure(configuration);
+builder.Services.AddSwaggerGen();
+builder.Services.AddAutoMapper(typeof(Program));
+
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+
+builder.Services.AddDbContextFactory(opts => opts.UseNpgsql(configuration["ConnectionString"]!));
+builder.Services.AddScoped, DbContextWrapper>();
+
+builder.Services.AddCors(options =>
+{
+ options.AddPolicy(
+ "CorsPolicy",
+ builder => builder
+ .SetIsOriginAllowed((host) => true)
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .AllowCredentials());
+});
+
+var app = builder.Build();
+
+app.UseSwagger();
+app.UseSwaggerUI();
+app.UseRouting();
+app.UseCors("CorsPolicy");
+
+app.UseEndpoints(endpoints =>
+{
+ endpoints.MapDefaultControllerRoute();
+ endpoints.MapControllers();
+});
+
+CreateDbIfNotExists(app);
+app.Run();
+
+IConfiguration GetConfiguration()
+{
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .AddEnvironmentVariables();
+
+ return builder.Build();
+}
+
+void CreateDbIfNotExists(IHost host)
+{
+ using (var scope = host.Services.CreateScope())
+ {
+ var services = scope.ServiceProvider;
+ try
+ {
+ var context = services.GetRequiredService();
+
+ DbInitializer.Initialize(context).Wait();
+ }
+ catch (BusinessException ex)
+ {
+ var logger = services.GetRequiredService>();
+ logger.LogError(ex, "An error occurred creating the DB.");
+ }
+ catch (Exception ex)
+ {
+ var logger = services.GetRequiredService>();
+ logger.LogError(ex, "An error occurred creating the DB.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/HomeWork34/Catalog/Catalog.Host/Repositories/CatalogBrandRepository.cs b/HomeWork34/Catalog/Catalog.Host/Repositories/CatalogBrandRepository.cs
new file mode 100644
index 0000000..149b01b
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Repositories/CatalogBrandRepository.cs
@@ -0,0 +1,86 @@
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Repositories.Interfaces;
+
+namespace Catalog.Host.Repositories
+{
+ public class CatalogBrandRepository : ICatalogBrandRepository
+ {
+ private readonly ApplicationDbContext _dbContext;
+ private readonly ILogger _logger;
+
+ public CatalogBrandRepository(IDbContextWrapper contextWrapper, ILogger logger)
+ {
+ _dbContext = contextWrapper.DbContext;
+ _logger = logger;
+ }
+
+ public async Task GetById(int? id)
+ {
+ CatalogBrand? responce;
+
+ try
+ {
+ responce = await _dbContext.CatalogBrands
+ .FirstOrDefaultAsync(f => f.Id == id);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex.Message);
+ throw new BusinessException(ex.Message);
+ }
+
+ return responce!;
+ }
+
+ public async Task> GetList()
+ {
+ return await _dbContext.CatalogBrands.ToListAsync();
+ }
+
+ public async Task AddAsync(string? brand)
+ {
+ if (brand is null)
+ {
+ _logger.LogWarning("brand null!");
+ throw new BusinessException("brand null!");
+ }
+
+ var entity = await _dbContext.CatalogBrands
+ .AddAsync(new CatalogBrand()
+ {
+ Brand = brand
+ });
+
+ await _dbContext.SaveChangesAsync();
+
+ return entity.Entity.Id;
+ }
+
+ public async Task DeleteAsync(int? id)
+ {
+ var entity = await GetById(id);
+
+ var message = _dbContext.CatalogBrands.Remove(entity!);
+ await _dbContext.SaveChangesAsync();
+
+ return message.State.ToString();
+ }
+
+ public async Task UpdateAsync(CatalogBrand? catalogBrand)
+ {
+ if (catalogBrand is null)
+ {
+ _logger.LogWarning("entity is null");
+ throw new BusinessException("brand null!");
+ }
+
+ var entity = await GetById(catalogBrand.Id);
+ entity!.Brand = catalogBrand.Brand;
+
+ await _dbContext.SaveChangesAsync();
+
+ return entity;
+ }
+ }
+}
diff --git a/HomeWork34/Catalog/Catalog.Host/Repositories/CatalogItemRepository.cs b/HomeWork34/Catalog/Catalog.Host/Repositories/CatalogItemRepository.cs
new file mode 100644
index 0000000..a67f58d
--- /dev/null
+++ b/HomeWork34/Catalog/Catalog.Host/Repositories/CatalogItemRepository.cs
@@ -0,0 +1,128 @@
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Repositories.Interfaces;
+
+namespace Catalog.Host.Repositories;
+
+public class CatalogItemRepository : ICatalogItemRepository
+{
+ private readonly ApplicationDbContext _dbContext;
+ private readonly ILogger