diff --git a/HomeWork32/eShop-Sample3/.dockerignore b/HomeWork32/eShop-Sample3/.dockerignore
new file mode 100644
index 0000000..cd967fc
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/.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/HomeWork32/eShop-Sample3/.editorconfig b/HomeWork32/eShop-Sample3/.editorconfig
new file mode 100644
index 0000000..6a5b933
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/.editorconfig
@@ -0,0 +1,4 @@
+[*.cs]
+
+# Default severity for analyzer diagnostics with category 'Usage'
+dotnet_analyzer_diagnostic.category-Usage.severity = none
diff --git a/HomeWork32/eShop-Sample3/.gitignore b/HomeWork32/eShop-Sample3/.gitignore
new file mode 100644
index 0000000..a5dbce6
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/.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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/.dockerignore b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/.dockerignore
new file mode 100644
index 0000000..cd967fc
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Catalog.Host.csproj b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Catalog.Host.csproj
new file mode 100644
index 0000000..7ef7825
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Catalog.Host.csproj
@@ -0,0 +1,37 @@
+
+
+
+ net8.0
+ enable
+ enable
+ Linux
+ ../../settings.ruleset
+ true
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Catalog.Host.csproj.user b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Catalog.Host.csproj.user
new file mode 100644
index 0000000..46b1b35
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Catalog.Host.csproj.user
@@ -0,0 +1,12 @@
+
+
+
+ ProjectDebugger
+
+
+ Catalog.Host
+
+
+ ProjectDebugger
+
+
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Configurations/CatalogConfig.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Configurations/CatalogConfig.cs
new file mode 100644
index 0000000..36df229
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Configurations/CatalogConfig.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Configurations;
+
+public class CatalogConfig
+{
+ public string Host { get; set; }
+ public string ImgUrl { get; set; }
+ public string ConnectionString { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogBffController.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogBffController.cs
new file mode 100644
index 0000000..07a82e4
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogBffController.cs
@@ -0,0 +1,61 @@
+using System.Net;
+using Catalog.Host.Data.Entities;
+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 CatalogBffController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly ICatalogService _catalogService;
+ private readonly ICatalogItemService _catalogItemService;
+ public CatalogBffController(
+ ILogger logger,
+ ICatalogService catalogService,
+ ICatalogItemService catalogItemService)
+ {
+ _logger = logger;
+ _catalogService = catalogService;
+ _catalogItemService = catalogItemService;
+
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(PaginatedItemsResponse), (int)HttpStatusCode.OK)]
+ public async Task Items(PaginatedItemsRequest request)
+ {
+ var result = await _catalogService.GetCatalogItemsAsync(request.PageSize, request.PageIndex);
+ return Ok(result);
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(PaginatedItemsResponse), (int)HttpStatusCode.OK)]
+ public async Task GetById(int id)
+ {
+ var result = await _catalogItemService.GetCatalogItemsByIdAsync(id);
+ return Ok(result);
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(PaginatedItemsResponse), (int)HttpStatusCode.OK)]
+ public async Task GetByBrand(int idBrand)
+ {
+ var result = await _catalogItemService.GetCatalogItemByBrandAsync(idBrand);
+ return Ok(result);
+ }
+
+ [HttpPost]
+ [ProducesResponseType(typeof(PaginatedItemsResponse), (int)HttpStatusCode.OK)]
+ public async Task GetByType(int idType)
+ {
+ var result = await _catalogItemService.GetCatalogItemByTypeAsync(idType);
+ return Ok(result);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogBrandController.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogBrandController.cs
new file mode 100644
index 0000000..99e978e
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogBrandController.cs
@@ -0,0 +1,38 @@
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Services.Interfaces;
+using Infrastructure;
+using Microsoft.AspNetCore.Mvc;
+
+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 type)
+ {
+ return await _service.AddAsync(type);
+ }
+
+ [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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogItemController.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogItemController.cs
new file mode 100644
index 0000000..1806a55
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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(new AddItemResponse() { Id = 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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogTypeController.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogTypeController.cs
new file mode 100644
index 0000000..04ff75c
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Controllers/CatalogTypeController.cs
@@ -0,0 +1,40 @@
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Services.Interfaces;
+using Infrastructure;
+using Microsoft.AspNetCore.Mvc;
+
+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)
+ {
+ return await _serivice.AddType(type);
+ }
+
+ [HttpPut]
+ public async Task UpdateType(CatalogTypeDto catalogType)
+ {
+ return await _serivice.UpdateType(catalogType);
+ }
+
+ [HttpDelete]
+ public async Task DeleteType(int id)
+ {
+ return await _serivice.DeleteType(id);
+ }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/ApplicationDbContext.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/ApplicationDbContext.cs
new file mode 100644
index 0000000..0e3750f
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/DbInitializer.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/DbInitializer.cs
new file mode 100644
index 0000000..5f63250
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/Entities/CatalogBrand.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/Entities/CatalogBrand.cs
new file mode 100644
index 0000000..9f63c1f
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/Entities/CatalogItem.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/Entities/CatalogItem.cs
new file mode 100644
index 0000000..959f5c0
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/Entities/CatalogType.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/Entities/CatalogType.cs
new file mode 100644
index 0000000..5eaddda
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs
new file mode 100644
index 0000000..2ea4be3
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs
new file mode 100644
index 0000000..0fc6e91
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs
new file mode 100644
index 0000000..1b92189
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/PaginatedItems.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Data/PaginatedItems.cs
new file mode 100644
index 0000000..130e77c
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Dockerfile b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Dockerfile
new file mode 100644
index 0000000..9e09d15
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Mapping/CatalogItemPictureResolver.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Mapping/CatalogItemPictureResolver.cs
new file mode 100644
index 0000000..4acb8a9
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Mapping/CatalogItemPictureResolver.cs
@@ -0,0 +1,22 @@
+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.Host}/{_config.ImgUrl}/{sourceMember}";
+ }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Mapping/MappingProfile.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Mapping/MappingProfile.cs
new file mode 100644
index 0000000..0adc55b
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.Designer.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.Designer.cs
new file mode 100644
index 0000000..bcd1d1f
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Migrations/20220108225624_InitialMigration.cs
new file mode 100644
index 0000000..df8f05e
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Migrations/ApplicationDbContextModelSnapshot.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Migrations/ApplicationDbContextModelSnapshot.cs
new file mode 100644
index 0000000..7298040
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogBrandDto.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogBrandDto.cs
new file mode 100644
index 0000000..8e4ad8f
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogBrandDto.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Models.Dtos;
+
+public class CatalogBrandDto
+{
+ public int Id { get; set; }
+
+ public string Brand { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogItemDto.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogItemDto.cs
new file mode 100644
index 0000000..b8f4d3d
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogItemDto.cs
@@ -0,0 +1,21 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Models.Dtos;
+
+public class CatalogItemDto
+{
+ 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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogTypeDto.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogTypeDto.cs
new file mode 100644
index 0000000..4e1d7b7
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Dtos/CatalogTypeDto.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS8618
+namespace Catalog.Host.Models.Dtos;
+
+public class CatalogTypeDto
+{
+ public int Id { get; set; }
+
+ public string Type { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Requests/CreateProductRequest.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Requests/CreateProductRequest.cs
new file mode 100644
index 0000000..20de15b
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/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/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Requests/PaginatedItemsRequest.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Requests/PaginatedItemsRequest.cs
new file mode 100644
index 0000000..f296711
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Requests/PaginatedItemsRequest.cs
@@ -0,0 +1,8 @@
+namespace Catalog.Host.Models.Requests;
+
+public class PaginatedItemsRequest
+{
+ public int PageIndex { get; set; }
+
+ public int PageSize { get; set; }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Response/AddItemResponse.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Response/AddItemResponse.cs
new file mode 100644
index 0000000..9e91f5a
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Response/AddItemResponse.cs
@@ -0,0 +1,6 @@
+namespace Catalog.Host.Models.Response;
+
+public class AddItemResponse
+{
+ public T Id { get; set; } = default(T) !;
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Response/PaginatedItemsResponse.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Response/PaginatedItemsResponse.cs
new file mode 100644
index 0000000..97683a2
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Models/Response/PaginatedItemsResponse.cs
@@ -0,0 +1,12 @@
+namespace Catalog.Host.Models.Response;
+
+public class PaginatedItemsResponse
+{
+ public int PageIndex { get; init; }
+
+ public int PageSize { get; init; }
+
+ public long Count { get; init; }
+
+ public IEnumerable Data { get; init; } = null!;
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Program.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Program.cs
new file mode 100644
index 0000000..2498c0b
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Program.cs
@@ -0,0 +1,70 @@
+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;
+using Microsoft.EntityFrameworkCore;
+
+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>();
+
+var app = builder.Build();
+
+app.UseSwagger();
+app.UseSwaggerUI();
+app.UseRouting();
+
+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 (Exception ex)
+ {
+ var logger = services.GetRequiredService>();
+ logger.LogError(ex, "An error occurred creating the DB.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogBrandRepository.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogBrandRepository.cs
new file mode 100644
index 0000000..7af02ea
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogBrandRepository.cs
@@ -0,0 +1,60 @@
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Repositories.Interfaces;
+using Catalog.Host.Services;
+using Catalog.Host.Services.Interfaces;
+using Microsoft.EntityFrameworkCore;
+
+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)
+ {
+ return await _dbContext.CatalogBrands
+ .FirstOrDefaultAsync(f => f.Id == id);
+ }
+
+ public async Task AddAsync(string brand)
+ {
+ 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)
+ {
+ var entity = await GetById(catalogBrand.Id);
+ entity!.Brand = catalogBrand.Brand;
+
+ await _dbContext.SaveChangesAsync();
+
+ return entity;
+ }
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogItemRepository.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogItemRepository.cs
new file mode 100644
index 0000000..cca3d6e
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogItemRepository.cs
@@ -0,0 +1,116 @@
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Repositories.Interfaces;
+using Catalog.Host.Services.Interfaces;
+using Microsoft.EntityFrameworkCore;
+
+namespace Catalog.Host.Repositories;
+
+public class CatalogItemRepository : ICatalogItemRepository
+{
+ private readonly ApplicationDbContext _dbContext;
+ private readonly ILogger _logger;
+
+ public CatalogItemRepository(
+ IDbContextWrapper dbContextWrapper,
+ ILogger logger)
+ {
+ _dbContext = dbContextWrapper.DbContext;
+ _logger = logger;
+ }
+
+ public async Task> GetByPageAsync(int pageIndex, int pageSize)
+ {
+ var totalItems = await _dbContext.CatalogItems
+ .LongCountAsync();
+
+ var itemsOnPage = await _dbContext.CatalogItems
+ .Include(i => i.CatalogBrand)
+ .Include(i => i.CatalogType)
+ .OrderBy(c => c.Name)
+ .Skip(pageSize * pageIndex)
+ .Take(pageSize)
+ .ToListAsync();
+
+ return new PaginatedItems() { TotalCount = totalItems, Data = itemsOnPage };
+ }
+
+ public async Task Add(
+ string name,
+ string description,
+ decimal price,
+ int availableStock,
+ int catalogBrandId,
+ int catalogTypeId,
+ string pictureFileName)
+ {
+ var item = await _dbContext.AddAsync(new CatalogItem
+ {
+ CatalogBrandId = catalogBrandId,
+ CatalogTypeId = catalogTypeId,
+ Description = description,
+ Name = name,
+ PictureFileName = pictureFileName,
+ Price = price
+ });
+
+ await _dbContext.SaveChangesAsync();
+
+ return item.Entity.Id;
+ }
+
+ public async Task GetCatalogItemsByIdAsync(int idItem)
+ {
+ return await _dbContext.CatalogItems
+ .Include(i => i.CatalogType)
+ .Include(i=>i.CatalogBrand)
+ .FirstOrDefaultAsync(item => item.Id == idItem);
+ }
+
+ public async Task> GetCatalogItemsByBrandAsync(int idBrand)
+ {
+ return await _dbContext.CatalogItems
+ .Include(i=>i.CatalogType)
+ .Include(i => i.CatalogBrand)
+ .Select(item => item)
+ .Where(item => item.CatalogBrand.Id == idBrand)
+ .ToListAsync();
+ }
+
+ public async Task> GetCatalogItemsByTypeAsync(int idType)
+ {
+ return await _dbContext.CatalogItems
+ .Include(i => i.CatalogBrand)
+ .Include(i => i.CatalogType)
+ .Select(item => item)
+ .Where(item => item.CatalogType.Id == idType)
+ .ToListAsync();
+ }
+
+ public async Task DeleteAsync(int id)
+ {
+ var item = await GetCatalogItemsByIdAsync(id);
+ var message = _dbContext.CatalogItems.Remove(item!);
+ await _dbContext.SaveChangesAsync();
+ return message.State.ToString();
+ }
+
+ public async Task Update(CatalogItem catalogItem)
+ {
+ var item = await GetCatalogItemsByIdAsync(catalogItem.Id);
+
+ item!.Price = catalogItem.Price;
+ item.Description = catalogItem.Description;
+ item.PictureFileName = catalogItem.PictureFileName;
+ item.Name = catalogItem.Name;
+ item.AvailableStock = catalogItem.AvailableStock;
+ item.CatalogBrandId = catalogItem.CatalogBrandId;
+ item.CatalogType = catalogItem.CatalogType;
+ item.CatalogTypeId = catalogItem.CatalogTypeId;
+ item.CatalogBrand = catalogItem.CatalogBrand;
+
+ await _dbContext.SaveChangesAsync();
+
+ return item;
+ }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogTypeRepository.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogTypeRepository.cs
new file mode 100644
index 0000000..ff575dd
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/CatalogTypeRepository.cs
@@ -0,0 +1,61 @@
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Repositories.Interfaces;
+using Catalog.Host.Services.Interfaces;
+using Microsoft.EntityFrameworkCore;
+
+namespace Catalog.Host.Repositories
+{
+ public class CatalogTypeRepository : ICatalogTypeRepository
+ {
+ private readonly ApplicationDbContext _dbContext;
+ private readonly ILogger _logger;
+
+ public CatalogTypeRepository(
+ IDbContextWrapper dbContextWrapper,
+ ILogger logger)
+ {
+ _dbContext = dbContextWrapper.DbContext;
+ _logger = logger;
+ }
+
+ public async Task GetById(int id)
+ {
+ return await _dbContext.CatalogTypes
+ .FirstOrDefaultAsync(f => f.Id == id);
+ }
+
+ public async Task AddTypeAsync(string type)
+ {
+ var entity = await _dbContext.CatalogTypes.AddAsync(new CatalogType()
+ {
+ Type = type
+ });
+
+ await _dbContext.SaveChangesAsync();
+
+ return entity.Entity.Id;
+ }
+
+ public async Task DeleteType(int id)
+ {
+ var entity = await GetById(id);
+ var message = _dbContext.CatalogTypes.Remove(entity!);
+
+ await _dbContext.SaveChangesAsync();
+
+ return message.State.ToString();
+ }
+
+ public async Task Update(CatalogType catalogType)
+ {
+ var entity = await GetById(catalogType.Id);
+ entity!.Type = catalogType.Type;
+
+ await _dbContext.SaveChangesAsync();
+
+ return entity;
+ }
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogBrandRepositorycs.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogBrandRepositorycs.cs
new file mode 100644
index 0000000..1d2ed77
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogBrandRepositorycs.cs
@@ -0,0 +1,12 @@
+using Catalog.Host.Data.Entities;
+
+namespace Catalog.Host.Repositories.Interfaces
+{
+ public interface ICatalogBrandRepository
+ {
+ Task GetById(int id);
+ Task AddAsync(string brand);
+ Task DeleteAsync(int id);
+ Task UpdateAsync(CatalogBrand catalogBrand);
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogItemRepository.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogItemRepository.cs
new file mode 100644
index 0000000..fff7551
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogItemRepository.cs
@@ -0,0 +1,15 @@
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+
+namespace Catalog.Host.Repositories.Interfaces;
+
+public interface ICatalogItemRepository
+{
+ Task> GetByPageAsync(int pageIndex, int pageSize);
+ Task Add(string name, string description, decimal price, int availableStock, int catalogBrandId, int catalogTypeId, string pictureFileName);
+ Task GetCatalogItemsByIdAsync(int id);
+ Task> GetCatalogItemsByBrandAsync(int idBrand);
+ Task> GetCatalogItemsByTypeAsync(int idType);
+ Task DeleteAsync(int id);
+ Task Update(CatalogItem catalogItem);
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogTypeRepository.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogTypeRepository.cs
new file mode 100644
index 0000000..c4631ec
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Repositories/Interfaces/ICatalogTypeRepository.cs
@@ -0,0 +1,12 @@
+using Catalog.Host.Data.Entities;
+
+namespace Catalog.Host.Repositories.Interfaces
+{
+ public interface ICatalogTypeRepository
+ {
+ Task GetById(int id);
+ Task AddTypeAsync(string type);
+ Task DeleteType(int id);
+ Task Update(CatalogType catalogType);
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/BaseDataService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/BaseDataService.cs
new file mode 100644
index 0000000..fd95c5c
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/BaseDataService.cs
@@ -0,0 +1,61 @@
+using Catalog.Host.Services.Interfaces;
+using Microsoft.EntityFrameworkCore;
+
+namespace Catalog.Host.Services;
+
+public abstract class BaseDataService
+ where T : DbContext
+{
+ private readonly IDbContextWrapper _dbContextWrapper;
+ private readonly ILogger> _logger;
+
+ protected BaseDataService(
+ IDbContextWrapper dbContextWrapper,
+ ILogger> logger)
+ {
+ _dbContextWrapper = dbContextWrapper;
+ _logger = logger;
+ }
+
+ protected Task ExecuteSafeAsync(Func action, CancellationToken cancellationToken = default) => ExecuteSafeAsync(token => action(), cancellationToken);
+
+ protected Task ExecuteSafeAsync(Func> action, CancellationToken cancellationToken = default) => ExecuteSafeAsync(token => action(), cancellationToken);
+
+ private async Task ExecuteSafeAsync(Func action, CancellationToken cancellationToken = default)
+ {
+ await using var transaction = await _dbContextWrapper.BeginTransactionAsync(cancellationToken);
+
+ try
+ {
+ await action(cancellationToken);
+
+ await transaction.CommitAsync(cancellationToken);
+ }
+ catch (Exception ex)
+ {
+ await transaction.RollbackAsync(cancellationToken);
+ _logger.LogError(ex, $"transaction is rollbacked");
+ }
+ }
+
+ private async Task ExecuteSafeAsync(Func> action, CancellationToken cancellationToken = default)
+ {
+ await using var transaction = await _dbContextWrapper.BeginTransactionAsync(cancellationToken);
+
+ try
+ {
+ var result = await action(cancellationToken);
+
+ await transaction.CommitAsync(cancellationToken);
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ await transaction.RollbackAsync(cancellationToken);
+ _logger.LogError(ex, $"transaction is rollbacked");
+ }
+
+ return default(TResult) !;
+ }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogBrandService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogBrandService.cs
new file mode 100644
index 0000000..13226ad
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogBrandService.cs
@@ -0,0 +1,52 @@
+using AutoMapper;
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Repositories.Interfaces;
+using Catalog.Host.Services.Interfaces;
+
+namespace Catalog.Host.Services
+{
+ public class CatalogBrandService : BaseDataService, ICatalogBrandService
+ {
+ private readonly ICatalogBrandRepository _repository;
+ private readonly IMapper _mapper;
+ public CatalogBrandService(
+ IDbContextWrapper dbContextWrapper,
+ ILogger logger,
+ ICatalogBrandRepository catalogBrandRepository,
+ IMapper mapper)
+ : base(dbContextWrapper, logger)
+ {
+ _repository = catalogBrandRepository;
+ _mapper = mapper;
+ }
+
+ public async Task AddAsync(string type)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ return await _repository.AddAsync(type);
+ });
+ }
+
+ public async Task DeleteAsync(int id)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ return await _repository.DeleteAsync(id);
+ });
+ }
+
+ public async Task UpdateAsync(CatalogBrandDto typeDto)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ var entity = _mapper.Map(typeDto);
+ var upentity = await _repository.UpdateAsync(entity);
+ var dto = _mapper.Map(upentity);
+ return dto;
+ });
+ }
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogItemService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogItemService.cs
new file mode 100644
index 0000000..10202c0
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogItemService.cs
@@ -0,0 +1,104 @@
+using AutoMapper;
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Repositories.Interfaces;
+using Catalog.Host.Services.Interfaces;
+
+namespace Catalog.Host.Services;
+
+public class CatalogItemService : BaseDataService, ICatalogItemService
+{
+ private readonly ICatalogItemRepository _catalogItemRepository;
+ private readonly IMapper _mapper;
+
+ public CatalogItemService(
+ IDbContextWrapper dbContextWrapper,
+ ILogger> logger,
+ ICatalogItemRepository catalogItemRepository,
+ IMapper mapper)
+ : base(dbContextWrapper, logger)
+ {
+ _catalogItemRepository = catalogItemRepository;
+ _mapper = mapper;
+ }
+
+ public Task Add(
+ string name,
+ string description,
+ decimal price,
+ int availableStock,
+ int catalogBrandId,
+ int catalogTypeId,
+ string pictureFileName)
+ {
+ return ExecuteSafeAsync(() => _catalogItemRepository.Add(
+ name,
+ description,
+ price,
+ availableStock,
+ catalogBrandId,
+ catalogTypeId,
+ pictureFileName));
+ }
+
+ public async Task GetCatalogItemsByIdAsync(int id)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ var item = await _catalogItemRepository.GetCatalogItemsByIdAsync(id);
+
+ return _mapper.Map(item);
+ });
+
+ }
+
+ public async Task> GetCatalogItemByBrandAsync(int idBrand)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ var itemColections = new List();
+
+ var items = await _catalogItemRepository.GetCatalogItemsByBrandAsync(idBrand);
+
+ itemColections.AddRange( items.Select(i => _mapper.Map(i)));
+
+ return itemColections;
+ });
+ }
+
+ public async Task> GetCatalogItemByTypeAsync(int idType)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ var itemColections = new List();
+
+ var items = await _catalogItemRepository.GetCatalogItemsByTypeAsync(idType);
+
+ itemColections.AddRange(items.Select(i => _mapper.Map(i)));
+
+ return itemColections;
+ });
+ }
+
+ public async Task DeleteAsync(int id)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ return await _catalogItemRepository.DeleteAsync(id);
+ });
+ }
+
+ public async Task UpdateAsync(CatalogItemDto catalogItemDto)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ var item = _mapper.Map(catalogItemDto);
+ item = await _catalogItemRepository.Update(item);
+ var dto = _mapper.Map(item);
+
+ return dto;
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogService.cs
new file mode 100644
index 0000000..d284b94
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogService.cs
@@ -0,0 +1,41 @@
+using AutoMapper;
+using Catalog.Host.Configurations;
+using Catalog.Host.Data;
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Models.Response;
+using Catalog.Host.Repositories.Interfaces;
+using Catalog.Host.Services.Interfaces;
+
+namespace Catalog.Host.Services;
+
+public class CatalogService : BaseDataService, ICatalogService
+{
+ private readonly ICatalogItemRepository _catalogItemRepository;
+ private readonly IMapper _mapper;
+
+ public CatalogService(
+ IDbContextWrapper dbContextWrapper,
+ ILogger> logger,
+ ICatalogItemRepository catalogItemRepository,
+ IMapper mapper)
+ : base(dbContextWrapper, logger)
+ {
+ _catalogItemRepository = catalogItemRepository;
+ _mapper = mapper;
+ }
+
+ public async Task> GetCatalogItemsAsync(int pageSize, int pageIndex)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ var result = await _catalogItemRepository.GetByPageAsync(pageIndex, pageSize);
+ return new PaginatedItemsResponse()
+ {
+ Count = result.TotalCount,
+ Data = result.Data.Select(s => _mapper.Map(s)).ToList(),
+ PageIndex = pageIndex,
+ PageSize = pageSize
+ };
+ });
+ }
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogTypeService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogTypeService.cs
new file mode 100644
index 0000000..02aa4f7
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/CatalogTypeService.cs
@@ -0,0 +1,53 @@
+using AutoMapper;
+using Catalog.Host.Data;
+using Catalog.Host.Data.Entities;
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Repositories.Interfaces;
+using Catalog.Host.Services.Interfaces;
+
+namespace Catalog.Host.Services
+{
+ public class CatalogTypeService : BaseDataService, ICatalogTypeService
+ {
+ private readonly ICatalogTypeRepository _repository;
+ private readonly IMapper _mapping;
+
+ public CatalogTypeService(
+ ICatalogTypeRepository repository,
+ IMapper mapper,
+ IDbContextWrapper dbContextWrapper,
+ ILogger logger)
+ : base(dbContextWrapper, logger)
+ {
+ _repository = repository;
+ _mapping = mapper;
+ }
+
+ public async Task AddType(string type)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ return await _repository.AddTypeAsync(type);
+ });
+ }
+
+ public async Task DeleteType(int id)
+ {
+ return await ExecuteSafeAsync(async() =>
+ {
+ return await _repository.DeleteType(id);
+ });
+ }
+
+ public async Task UpdateType(CatalogTypeDto typeDto)
+ {
+ return await ExecuteSafeAsync(async () =>
+ {
+ var entity = _mapping.Map(typeDto);
+ var upentity = await _repository.Update(entity);
+ var dto = _mapping.Map(upentity);
+ return dto;
+ });
+ }
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/DbContextWrapper.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/DbContextWrapper.cs
new file mode 100644
index 0000000..90bb3e0
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/DbContextWrapper.cs
@@ -0,0 +1,24 @@
+using Catalog.Host.Services.Interfaces;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Catalog.Host.Services;
+
+public class DbContextWrapper : IDbContextWrapper
+ where T : DbContext
+{
+ private readonly T _dbContext;
+
+ public DbContextWrapper(
+ IDbContextFactory dbContextFactory)
+ {
+ _dbContext = dbContextFactory.CreateDbContext();
+ }
+
+ public T DbContext => _dbContext;
+
+ public Task BeginTransactionAsync(CancellationToken cancellationToken)
+ {
+ return _dbContext.Database.BeginTransactionAsync(cancellationToken);
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogBrandService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogBrandService.cs
new file mode 100644
index 0000000..3336b93
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogBrandService.cs
@@ -0,0 +1,11 @@
+using Catalog.Host.Models.Dtos;
+
+namespace Catalog.Host.Services.Interfaces
+{
+ public interface ICatalogBrandService
+ {
+ Task AddAsync(string type);
+ Task DeleteAsync(int id);
+ Task UpdateAsync(CatalogBrandDto typeDto);
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogItemService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogItemService.cs
new file mode 100644
index 0000000..9b91305
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogItemService.cs
@@ -0,0 +1,13 @@
+using Catalog.Host.Models.Dtos;
+
+namespace Catalog.Host.Services.Interfaces;
+
+public interface ICatalogItemService
+{
+ Task Add(string name, string description, decimal price, int availableStock, int catalogBrandId, int catalogTypeId, string pictureFileName);
+ Task GetCatalogItemsByIdAsync(int id);
+ Task> GetCatalogItemByBrandAsync(int idBrand);
+ Task> GetCatalogItemByTypeAsync(int idType);
+ Task UpdateAsync(CatalogItemDto catalogItemDto);
+ Task DeleteAsync(int id);
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogService.cs
new file mode 100644
index 0000000..2189d93
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogService.cs
@@ -0,0 +1,9 @@
+using Catalog.Host.Models.Dtos;
+using Catalog.Host.Models.Response;
+
+namespace Catalog.Host.Services.Interfaces;
+
+public interface ICatalogService
+{
+ Task> GetCatalogItemsAsync(int pageSize, int pageIndex);
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogTypeService.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogTypeService.cs
new file mode 100644
index 0000000..5074132
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/ICatalogTypeService.cs
@@ -0,0 +1,11 @@
+using Catalog.Host.Models.Dtos;
+
+namespace Catalog.Host.Services.Interfaces
+{
+ public interface ICatalogTypeService
+ {
+ Task UpdateType(CatalogTypeDto typeDto);
+ Task DeleteType(int id);
+ Task AddType(string type);
+ }
+}
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/IDbContextWrapper.cs b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/IDbContextWrapper.cs
new file mode 100644
index 0000000..8044df5
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/Services/Interfaces/IDbContextWrapper.cs
@@ -0,0 +1,11 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Catalog.Host.Services.Interfaces;
+
+public interface IDbContextWrapper
+ where T : DbContext
+{
+ T DbContext { get; }
+ Task BeginTransactionAsync(CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/appsettings.json b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/appsettings.json
new file mode 100644
index 0000000..4112043
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Catalog/Catalog.Host/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "Host": "www.alevelwebsite.com",
+ "ImgUrl": "assets/img",
+ "ConnectionString": "server=www.alevelwebsite.com;port=5433;database=catalog;uid=postgres;password=postgres;",
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/HomeWork32/eShop-Sample3/Infrastructure/Infrastructure/ComponentDefaults.cs b/HomeWork32/eShop-Sample3/Infrastructure/Infrastructure/ComponentDefaults.cs
new file mode 100644
index 0000000..677b560
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Infrastructure/Infrastructure/ComponentDefaults.cs
@@ -0,0 +1,6 @@
+namespace Infrastructure;
+
+public static class ComponentDefaults
+{
+ public const string DefaultRoute = "api/v1/[controller]/[action]";
+}
diff --git a/HomeWork32/eShop-Sample3/Infrastructure/Infrastructure/Infrastructure.csproj b/HomeWork32/eShop-Sample3/Infrastructure/Infrastructure/Infrastructure.csproj
new file mode 100644
index 0000000..8db8f0b
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/Infrastructure/Infrastructure/Infrastructure.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net8.0
+ enable
+ enable
+ Infrastructure
+ Infrastructure
+
+
+
+
+ ..\..\..\..\..\..\usr\local\share\dotnet\shared\Microsoft.AspNetCore.App\6.0.1\Microsoft.Extensions.Configuration.Abstractions.dll
+
+
+ ..\..\..\..\..\..\usr\local\share\dotnet\shared\Microsoft.AspNetCore.App\6.0.1\Microsoft.Extensions.DependencyInjection.Abstractions.dll
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
diff --git a/HomeWork32/eShop-Sample3/README.md b/HomeWork32/eShop-Sample3/README.md
new file mode 100644
index 0000000..a16d68b
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/README.md
@@ -0,0 +1,23 @@
+#!!!!!SETUP STEPS!!!!!
+Update host file on your PC
+ like this instruction https://www.nublue.co.uk/guides/edit-hosts-file/#:~:text=In%20Windows%2010%20the%20hosts,%5CDrivers%5Cetc%5Chosts.
+
+Need to path these lines
+
+ 127.0.0.1 www.alevelwebsite.com
+ 0.0.0.0 www.alevelwebsite.com
+ 192.168.0.4 www.alevelwebsite.com
+
+#docker
+docker-compose build --no-cache
+
+docker-compose up
+
+#Add-Migration
+dotnet ef --startup-project Catalog/Catalog.Host migrations add InitialMigration --project Catalog/Catalog.Host
+
+#Update-Migration
+dotnet ef --startup-project Catalog/Catalog.Host database update InitialMigration --project Catalog/Catalog.Host
+
+#Remove-Migration
+dotnet ef --startup-project Catalog/Catalog.Host migrations remove --project Catalog/Catalog.Host -f
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/docker-compose.yml b/HomeWork32/eShop-Sample3/docker-compose.yml
new file mode 100644
index 0000000..40607bb
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/docker-compose.yml
@@ -0,0 +1,47 @@
+version: '3.8'
+
+services:
+ catalog.api:
+ container_name: lde.catalog.api
+ build:
+ context: .
+ dockerfile: Catalog/Catalog.Host/Dockerfile
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://+:5000
+ depends_on:
+ - postgres
+ ports:
+ - 5000:5000
+ nginx:
+ image: nginx
+ container_name: lde.nginx
+ ports:
+ - 80:80
+ pgadmin4:
+ image: dpage/pgadmin4
+ container_name: lde.pgadmin4
+ environment:
+ PGADMIN_DEFAULT_EMAIL: user@domain.com
+ PGADMIN_DEFAULT_PASSWORD: postgres
+ depends_on:
+ - postgres
+ ports:
+ - 8001:80
+ postgres:
+ image: postgres
+ container_name: lde.postgres
+ environment:
+ POSTGRES_PASSWORD: postgres
+ ports:
+ - 5433:5432
+networks:
+ default:
+ driver: bridge
+ internal: false
+ attachable: true
+ ipam:
+ driver: default
+ config:
+ - subnet: 192.168.0.0/24
+ gateway: 192.168.0.1
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/eShop.sln b/HomeWork32/eShop-Sample3/eShop.sln
new file mode 100644
index 0000000..27ae592
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/eShop.sln
@@ -0,0 +1,51 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.8.34525.116
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Catalog", "Catalog", "{E769E2A0-66AF-44D6-9300-C45D3889E06A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalog.Host", "Catalog\Catalog.Host\Catalog.Host.csproj", "{6A8E9683-762E-4E83-BD60-75822AC8265D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{DCFFDCA1-598D-4EA6-AAF6-4BE647B27C32}"
+ ProjectSection(SolutionItems) = preProject
+ docker-compose.yml = docker-compose.yml
+ README.md = README.md
+ settings.ruleset = settings.ruleset
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{A6C7B2CD-2199-497C-9EF5-32274390D2D7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "Infrastructure\Infrastructure\Infrastructure.csproj", "{C877F308-7870-433C-AA13-DE7DBC3675B9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE1C6FA0-60FF-41BF-BA2C-7200F868E922}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6A8E9683-762E-4E83-BD60-75822AC8265D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6A8E9683-762E-4E83-BD60-75822AC8265D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6A8E9683-762E-4E83-BD60-75822AC8265D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6A8E9683-762E-4E83-BD60-75822AC8265D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C877F308-7870-433C-AA13-DE7DBC3675B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C877F308-7870-433C-AA13-DE7DBC3675B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C877F308-7870-433C-AA13-DE7DBC3675B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C877F308-7870-433C-AA13-DE7DBC3675B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {6A8E9683-762E-4E83-BD60-75822AC8265D} = {E769E2A0-66AF-44D6-9300-C45D3889E06A}
+ {C877F308-7870-433C-AA13-DE7DBC3675B9} = {A6C7B2CD-2199-497C-9EF5-32274390D2D7}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {ACE14773-8677-4216-9C1E-C1423562D84A}
+ EndGlobalSection
+EndGlobal
diff --git a/HomeWork32/eShop-Sample3/eShop.sln.DotSettings b/HomeWork32/eShop-Sample3/eShop.sln.DotSettings
new file mode 100644
index 0000000..06b71d5
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/eShop.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ False
\ No newline at end of file
diff --git a/HomeWork32/eShop-Sample3/settings.ruleset b/HomeWork32/eShop-Sample3/settings.ruleset
new file mode 100644
index 0000000..fe6f504
--- /dev/null
+++ b/HomeWork32/eShop-Sample3/settings.ruleset
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file