[
  {
    "path": ".dockerignore",
    "content": "**/.classpath\n**/.dockerignore\n**/.env\n**/.git\n**/.gitignore\n**/.project\n**/.settings\n**/.toolstarget\n**/.vs\n**/.vscode\n**/*.*proj.user\n**/*.dbmdl\n**/*.jfm\n**/azds.yaml\n**/bin\n**/charts\n**/docker-compose*\n**/Dockerfile*\n**/node_modules\n**/npm-debug.log\n**/obj\n**/secrets.dev.yaml\n**/values.dev.yaml\nLICENSE\nREADME.md\n!**/.gitignore\n!.git/HEAD\n!.git/config\n!.git/packed-refs\n!.git/refs/heads/**"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Ww][Ii][Nn]32/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Oo]ut/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# ASP.NET Scaffolding\nScaffoldingReadMe.txt\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Coverlet is a free, cross platform Code Coverage Tool\ncoverage*.json\ncoverage*.xml\ncoverage*.info\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Fody - auto-generated XML schema\nFodyWeavers.xsd"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/Dockerfile",
    "content": "#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.\n\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nUSER app\nWORKDIR /app\nEXPOSE 8080\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nARG BUILD_CONFIGURATION=Release\nWORKDIR /src\nCOPY [\"ApiGateways/OcelotApiGateway/OcelotApiGateway.csproj\", \"ApiGateways/OcelotApiGateway/\"]\nRUN dotnet restore \"./ApiGateways/OcelotApiGateway/./OcelotApiGateway.csproj\"\nCOPY . .\nWORKDIR \"/src/ApiGateways/OcelotApiGateway\"\nRUN dotnet build \"./OcelotApiGateway.csproj\" -c $BUILD_CONFIGURATION -o /app/build\n\nFROM build AS publish\nARG BUILD_CONFIGURATION=Release\nRUN dotnet publish \"./OcelotApiGateway.csproj\" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"OcelotApiGateway.dll\"]"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/OcelotApiGateway.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>\n    <DockerfileContext>..\\..</DockerfileContext>\n    <DockerComposeProjectPath>..\\..\\docker-compose.dcproj</DockerComposeProjectPath>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.VisualStudio.Azure.Containers.Tools.Targets\" Version=\"1.19.5\" />\n    <PackageReference Include=\"Ocelot\" Version=\"22.0.1\" />\n    <PackageReference Include=\"Ocelot.Cache.CacheManager\" Version=\"22.0.1\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/Program.cs",
    "content": "using Ocelot.Cache.CacheManager;\nusing Ocelot.DependencyInjection;\nusing Ocelot.Middleware;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Logging\n    .AddConfiguration(builder.Configuration.GetSection(\"Logging\"))\n    .AddConsole()\n    .AddDebug();\n\nbuilder.Configuration\n    .AddJsonFile($\"ocelot.{builder.Environment.EnvironmentName}.json\");\n\nbuilder.Services\n    .AddOcelot()\n    .AddCacheManager(x =>\n    {\n        x.WithDictionaryHandle();\n    });\n\nvar app = builder.Build();\n\nawait app.UseOcelot();\n\napp.Run();\n"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"OcelotApiGateway\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Local\"\n      },\n      \"dotnetRunMessages\": true,\n      \"applicationUrl\": \"http://localhost:5010\"\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Local\"\n      }\n    },\n    \"Docker\": {\n      \"commandName\": \"Docker\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"{Scheme}://{ServiceHost}:{ServicePort}\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_HTTP_PORTS\": \"8010\"\n      },\n      \"publishAllPorts\": true\n    }\n  },\n  \"$schema\": \"http://json.schemastore.org/launchsettings.json\",\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:13294\",\n      \"sslPort\": 0\n    }\n  }\n}"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/appsettings.Development.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  }\n}\n"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}\n"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/ocelot.Development.json",
    "content": "// BFF: Backend For Frontend\n{\n  \"Routes\": [\n    // Catalog.Api\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"catalog.api\",\n          \"Port\": 8000\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Catalog/{controller}/{action}\",\n      \"UpStreamHttpMethod\": [ \"GET\", \"POST\", \"PUT\" ],\n      \"FileCacheOptions\": {\n        \"TtlSeconds\": 30\n      }\n    },\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}/{idOrNameOrCategory}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"catalog.api\",\n          \"Port\": 8000\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Catalog/{controller}/{action}/{idOrNameOrCategory}\",\n      \"UpStreamHttpMethod\": [ \"GET\", \"DELETE\" ]\n    },\n    // Basket.Api\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"basket.api\",\n          \"Port\": 8001\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Basket/{controller}/{action}\",\n      \"UpStreamHttpMethod\": [ \"POST\" ],\n      \"RateLimitOptions\": {\n        \"ClientWhiteList\": [],\n        \"EnableRateLimiting\": true,\n        \"Period\": \"5s\",\n        \"PeriodTimeSpan\": 1,\n        \"Limit\": 1\n      }\n    },\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}/{userName}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"basket.api\",\n          \"Port\": 8001\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Basket/{controller}/{action}/{userName}\",\n      \"UpStreamHttpMethod\": [ \"GET\", \"DELETE\" ]\n    },\n    // Discount.Api\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"discount.api\",\n          \"Port\": 8002\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Discount/{controller}/{action}\",\n      \"UpStreamHttpMethod\": [ \"POST\", \"PUT\" ]\n    },\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}/{productName}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"discount.api\",\n          \"Port\": 8002\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Discount/{controller}/{action}/{productName}\",\n      \"UpStreamHttpMethod\": [ \"GET\", \"DELETE\" ]\n    },\n    // Ordering.Api\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}/{userName}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"ordering.api\",\n          \"Port\": 8004\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Ordering/{controller}/{action}/{userName}\",\n      \"UpStreamHttpMethod\": [ \"GET\" ]\n    }\n  ],\n  \"GlobalConfiguration\": {\n    \"BaseUrl\": \"http://localhost:5010\"\n  }\n}"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/ocelot.Local.json",
    "content": "// BFF: Backend For Frontend\n{\n  \"Routes\": [\n    // Catalog.Api\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 8000\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Catalog/{controller}/{action}\",\n      \"UpStreamHttpMethod\": [ \"GET\", \"POST\", \"PUT\" ],\n      \"FileCacheOptions\": {\n        \"TtlSeconds\": 30\n      }\n    },\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}/{idOrNameOrCategory}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 8000\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Catalog/{controller}/{action}/{idOrNameOrCategory}\",\n      \"UpStreamHttpMethod\": [ \"GET\", \"DELETE\" ]\n    },\n    // Basket.Api\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 8001\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Basket/{controller}/{action}\",\n      \"UpStreamHttpMethod\": [ \"POST\" ],\n      \"RateLimitOptions\": {\n        \"ClientWhiteList\": [],\n        \"EnableRateLimiting\": true,\n        \"Period\": \"5s\",\n        \"PeriodTimeSpan\": 1,\n        \"Limit\": 1\n      }\n    },\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}/{userName}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 8001\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Basket/{controller}/{action}/{userName}\",\n      \"UpStreamHttpMethod\": [ \"GET\", \"DELETE\" ]\n    },\n    // Discount.Api\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 8002\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Discount/{controller}/{action}\",\n      \"UpStreamHttpMethod\": [ \"POST\", \"PUT\" ]\n    },\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}/{productName}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 8002\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Discount/{controller}/{action}/{productName}\",\n      \"UpStreamHttpMethod\": [ \"GET\", \"DELETE\" ]\n    },\n    // Ordering.Api\n    {\n      \"DownStreamPathTemplate\": \"/api/v1/{controller}/{action}/{userName}\",\n      \"DownStreamScheme\": \"http\",\n      \"DownStreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 8004\n        }\n      ],\n      \"UpStreamPathTemplate\": \"/Ordering/{controller}/{action}/{userName}\",\n      \"UpStreamHttpMethod\": [ \"GET\" ]\n    }\n  ],\n  \"GlobalConfiguration\": {\n    \"BaseUrl\": \"http://localhost:5010\"\n  }\n}"
  },
  {
    "path": "ApiGateways/OcelotApiGateway/ocelot.json",
    "content": ""
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Controllers/ShoppingController.cs",
    "content": "﻿using Microsoft.AspNetCore.Mvc;\nusing OnlineShop.Aggregator.DTOs;\nusing OnlineShop.Aggregator.Services;\nusing System.Net;\n\nnamespace OnlineShop.Aggregator.Controllers;\n\n[ApiController]\n[Route(\"api/v1/[controller]/[action]\")]\npublic class ShoppingController(\n    ICatalogService _catalogService,\n    IBasketService _basketService,\n    IOrderService _orderService\n    ) : ControllerBase\n{\n    [HttpGet(\"{userName}\")]\n    [ProducesResponseType(typeof(ShoppingDTO), (int)HttpStatusCode.OK)]\n    public async Task<ActionResult<ShoppingDTO>> GetByUserName(string userName)\n    {\n        var basket = await _basketService.GetBasket(userName);\n        foreach (var basketItem in basket.Items)\n        {\n            var product = await _catalogService.GetCatalog(basketItem.ProductId);\n\n            basketItem.ProductName = product.Name;\n            basketItem.Category = product.Category;\n            basketItem.Summary = product.Summary;\n            basketItem.Description = product.Description;\n            basketItem.ImageFile = product.ImageFile;\n        }\n\n        var orders = await _orderService.GetOrderByUserName(userName);\n\n        var shoppingDTO = new ShoppingDTO\n        {\n            UserName = userName,\n            BasketWithProduct = basket,\n            Orders = orders \n        };\n\n        return Ok(shoppingDTO);\n    }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/DTOs/BasketDTO.cs",
    "content": "﻿namespace OnlineShop.Aggregator.DTOs;\n\npublic class BasketDTO\n{\n    public string UserName { get; set; } = string.Empty;\n    public List<BasketItemExtendedDTO> Items { get; set; } = [];\n    public decimal TotalPrice { get; set; }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/DTOs/BasketItemExtendedDTO.cs",
    "content": "﻿namespace OnlineShop.Aggregator.DTOs;\n\npublic class BasketItemExtendedDTO\n{\n    public int Quantity { get; set; }\n    public string Color { get; set; } = string.Empty;\n    public decimal Price { get; set; }\n    public string ProductId { get; set; } = string.Empty;\n    public string ProductName { get; set; } = string.Empty;\n\n    // additional data\n    public string Category { get; set; } = string.Empty;\n    public string Summary { get; set; } = string.Empty;\n    public string Description { get; set; } = string.Empty;\n    public string ImageFile { get; set; } = string.Empty;\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/DTOs/CatalogDTO.cs",
    "content": "﻿namespace OnlineShop.Aggregator.DTOs;\n\npublic class CatalogDTO\n{\n    public string Id { get; set; } = string.Empty;\n    public string Name { get; set; } = string.Empty;\n    public string Category { get; set; } = string.Empty;\n    public string Summary { get; set; } = string.Empty;\n    public string Description { get; set; } = string.Empty;\n    public string ImageFile { get; set; } = string.Empty;\n    public decimal Price { get; set; }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/DTOs/OrderResponseDTO.cs",
    "content": "﻿namespace OnlineShop.Aggregator.DTOs;\n\npublic class OrderResponseDTO\n{\n    public string UserName { get; set; } = string.Empty;\n    public decimal TotalPrice { get; set; }\n\n    public string FirstName { get; set; } = string.Empty;\n    public string LastName { get; set; } = string.Empty;\n    public string EmailAddress { get; set; } = string.Empty;\n    public string Country { get; set; } = string.Empty;\n    public string City { get; set; } = string.Empty;\n\n    public string BankName { get; set; } = string.Empty;\n    public string RefCode { get; set; } = string.Empty;\n    public int PaymentMethod { get; set; }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/DTOs/ShoppingDTO.cs",
    "content": "﻿namespace OnlineShop.Aggregator.DTOs;\n\npublic class ShoppingDTO\n{\n    public string UserName { get; set; } = string.Empty;\n    public required BasketDTO BasketWithProduct { get; set; }\n    public IEnumerable<OrderResponseDTO> Orders { get; set; } = [];\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Dockerfile",
    "content": "#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.\n\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nUSER app\nWORKDIR /app\nEXPOSE 8080\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nARG BUILD_CONFIGURATION=Release\nWORKDIR /src\nCOPY [\"ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/OnlineShop.Aggregator.csproj\", \"ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/\"]\nRUN dotnet restore \"./ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/./OnlineShop.Aggregator.csproj\"\nCOPY . .\nWORKDIR \"/src/ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator\"\nRUN dotnet build \"./OnlineShop.Aggregator.csproj\" -c $BUILD_CONFIGURATION -o /app/build\n\nFROM build AS publish\nARG BUILD_CONFIGURATION=Release\nRUN dotnet publish \"./OnlineShop.Aggregator.csproj\" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"OnlineShop.Aggregator.dll\"]"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Extensions/HttpClientExtensions.cs",
    "content": "﻿using System.Text.Json;\n\nnamespace OnlineShop.Aggregator.Extensions;\n\npublic static class HttpClientExtensions\n{\n    public static async Task<T> ReadContentAs<T>(this HttpResponseMessage response)\n    {\n        if (!response.IsSuccessStatusCode)\n            throw new ApplicationException($\"Something went wrong when calling api, {response.ReasonPhrase}\");\n\n        var dataAsString = await response.Content.ReadAsStringAsync();\n\n        return JsonSerializer.Deserialize<T>(dataAsString)!;\n    }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/OnlineShop.Aggregator.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <InvariantGlobalization>true</InvariantGlobalization>\n    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>\n    <DockerfileContext>..\\..\\..</DockerfileContext>\n    <DockerComposeProjectPath>..\\..\\..\\docker-compose.dcproj</DockerComposeProjectPath>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.VisualStudio.Azure.Containers.Tools.Targets\" Version=\"1.19.5\" />\n    <PackageReference Include=\"Swashbuckle.AspNetCore\" Version=\"6.4.0\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/OnlineShop.Aggregator.http",
    "content": "@OnlineShop.Aggregator_HostAddress = http://localhost:5140\n\nGET {{OnlineShop.Aggregator_HostAddress}}/weatherforecast/\nAccept: application/json\n\n###\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Program.cs",
    "content": "using OnlineShop.Aggregator.Services;\nusing System.Text.Json;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllers();\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nbuilder.Services.AddOptions<JsonSerializerOptions>()\n    .Configure<IServiceProvider>((options, serviceProvider) =>\n    {\n        options.PropertyNameCaseInsensitive = true;\n    });\n\nbuilder.Services.AddHttpClient<ICatalogService, CatalogService>(c =>\n{\n    c.BaseAddress = new Uri(builder.Configuration.GetValue<string>(\"ApiSettings:CatalogUrl\") ?? \"\");\n});\nbuilder.Services.AddHttpClient<IBasketService, BasketService>(c =>\n{\n    c.BaseAddress = new Uri(builder.Configuration.GetValue<string>(\"ApiSettings:BasketUrl\") ?? \"\");\n});\nbuilder.Services.AddHttpClient<IOrderService, OrderService>(c =>\n{\n    c.BaseAddress = new Uri(builder.Configuration.GetValue<string>(\"ApiSettings:OrderingUrl\") ?? \"\");\n});\n\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.MapControllers();\n\napp.Run();\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"OnlineShop.Aggregator\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"dotnetRunMessages\": true,\n      \"applicationUrl\": \"http://localhost:5005\"\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"Docker\": {\n      \"commandName\": \"Docker\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"{Scheme}://{ServiceHost}:{ServicePort}/swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_HTTP_PORTS\": \"8005\"\n      },\n      \"publishAllPorts\": true\n    }\n  },\n  \"$schema\": \"http://json.schemastore.org/launchsettings.json\",\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:49114\",\n      \"sslPort\": 0\n    }\n  }\n}"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Services/BasketService.cs",
    "content": "﻿using OnlineShop.Aggregator.DTOs;\nusing OnlineShop.Aggregator.Extensions;\n\nnamespace OnlineShop.Aggregator.Services;\n\npublic class BasketService(HttpClient _client) : IBasketService\n{\n    public async Task<BasketDTO> GetBasket(string userName)\n    {\n        var response = await _client.GetAsync($\"/api/v1/Order/GetByUserName/{userName}\");\n        return await response.ReadContentAs<BasketDTO>();\n    }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Services/CatalogService.cs",
    "content": "﻿using OnlineShop.Aggregator.DTOs;\nusing OnlineShop.Aggregator.Extensions;\n\nnamespace OnlineShop.Aggregator.Services;\n\npublic class CatalogService(HttpClient _client) : ICatalogService\n{\n    public async Task<IEnumerable<CatalogDTO>> GetCatalog()\n    {\n        var response = await _client.GetAsync(\"/api/v1/Product/GetAll\");\n        return await response.ReadContentAs<List<CatalogDTO>>();\n    }\n\n    public async Task<CatalogDTO> GetCatalog(string id)\n    {\n        var response = await _client.GetAsync($\"/api/v1/Product/GetById/{id}\");\n        return await response.ReadContentAs<CatalogDTO>();\n    }\n\n    public async Task<IEnumerable<CatalogDTO>> GetCatalogByCategory(string category)\n    {\n        var response = await _client.GetAsync($\"/api/v1/Product/GetByCategory/{category}\");\n        return await response.ReadContentAs<List<CatalogDTO>>();\n    }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Services/IBasketService.cs",
    "content": "﻿using OnlineShop.Aggregator.DTOs;\n\nnamespace OnlineShop.Aggregator.Services;\n\npublic interface IBasketService\n{\n    Task<BasketDTO> GetBasket(string userName);\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Services/ICatalogService.cs",
    "content": "﻿using OnlineShop.Aggregator.DTOs;\n\nnamespace OnlineShop.Aggregator.Services;\n\npublic interface ICatalogService\n{\n    Task<IEnumerable<CatalogDTO>> GetCatalog();\n    Task<CatalogDTO> GetCatalog(string id);\n    Task<IEnumerable<CatalogDTO>> GetCatalogByCategory(string category);\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Services/IOrderService.cs",
    "content": "﻿using OnlineShop.Aggregator.DTOs;\n\nnamespace OnlineShop.Aggregator.Services;\n\npublic interface IOrderService\n{\n    Task<IEnumerable<OrderResponseDTO>> GetOrderByUserName(string userName);\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Services/OrderService.cs",
    "content": "﻿using OnlineShop.Aggregator.DTOs;\nusing OnlineShop.Aggregator.Extensions;\n\nnamespace OnlineShop.Aggregator.Services;\n\npublic class OrderService(HttpClient _client) : IOrderService\n{\n    public async Task<IEnumerable<OrderResponseDTO>> GetOrderByUserName(string userName)\n    {\n        var response = await _client.GetAsync($\"/api/v1/Order/GetOrdersByUserName/{userName}\");\n        return await response.ReadContentAs<List<OrderResponseDTO>>();\n    }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/appsettings.Development.json",
    "content": "{\n  \"ApiSettings\": {\n    \"CatalogUrl\": \"http://localhost:8000\",\n    \"BasketUrl\": \"http://localhost:8001\",\n    \"OrderingUrl\": \"http://localhost:8004\"\n  },\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  }\n}\n"
  },
  {
    "path": "ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}\n"
  },
  {
    "path": "BuildingBlocks/EventBus.Messages/Common/EventBusConstant.cs",
    "content": "﻿namespace EventBus.Messages.Common;\n\npublic class EventBusConstant\n{\n    public const string BasketCheckoutQueue = \"BasketCheckout-queue\";\n}\n"
  },
  {
    "path": "BuildingBlocks/EventBus.Messages/EventBus.Messages.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "BuildingBlocks/EventBus.Messages/Events/BasketCheckoutEvent.cs",
    "content": "﻿namespace EventBus.Messages.Events;\n\npublic class BasketCheckoutEvent : IntegrationBaseEvent\n{\n    public string UserName { get; set; } = string.Empty;\n    public decimal TotalPrice { get; set; }\n\n    public string FirstName { get; set; } = string.Empty;\n    public string LastName { get; set; } = string.Empty;\n    public string EmailAddress { get; set; } = string.Empty;\n    public string Country { get; set; } = string.Empty;\n    public string City { get; set; } = string.Empty;\n\n    public string BankName { get; set; } = string.Empty;\n    public string RefCode { get; set; } = string.Empty;\n    public int PaymentMethod { get; set; }\n}\n"
  },
  {
    "path": "BuildingBlocks/EventBus.Messages/Events/IntegrationBaseEvent.cs",
    "content": "﻿namespace EventBus.Messages.Events;\n\npublic class IntegrationBaseEvent\n{\n    public IntegrationBaseEvent()\n    {\n        Id = Guid.NewGuid();\n        CreatedAt = DateTime.Now;\n    }\n\n    public IntegrationBaseEvent(Guid id, DateTime createdAt)\n    {\n        Id = id;\n        CreatedAt = createdAt;\n    }\n\n    public Guid Id { get; private set; }\n    public DateTime CreatedAt { get; private set; }\n}\n"
  },
  {
    "path": "OnlineShop.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.7.34221.43\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Services\", \"Services\", \"{087760BA-AF35-40D8-8779-01B3C48D3EF4}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Catalog\", \"Catalog\", \"{653AE14C-8DD7-4E9C-96B5-D6B99C1D0CF7}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Catalog.Api\", \"Services\\Catalog\\Catalog.Api\\Catalog.Api.csproj\", \"{A6F2D049-4B6E-4805-889F-49D113927DA6}\"\nEndProject\nProject(\"{E53339B2-1760-4266-BCC7-CA923CBCF16C}\") = \"docker-compose\", \"docker-compose.dcproj\", \"{9ED1D254-4DE6-4229-B3B8-A60383414787}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Basket\", \"Basket\", \"{9830C312-7C6A-4AAE-9700-AF8E2BCF942D}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Basket.Api\", \"Services\\Basket\\Basket.Api\\Basket.Api.csproj\", \"{820840D7-0105-4611-B956-235D6EE15191}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Discount\", \"Discount\", \"{4166A6E7-178A-4FDE-B7BD-33C5833F9472}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Discount.Api\", \"Services\\Discount\\Discount.Api\\Discount.Api.csproj\", \"{C8BD72CC-5DDA-4A81-8A5B-FB411D1707ED}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Discount.Grpc\", \"Services\\Discount\\Discount.Grpc\\Discount.Grpc.csproj\", \"{DAD4D4A1-9AEB-46CF-A143-D19C1EB9C36D}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Ordering\", \"Ordering\", \"{69172560-B5A3-488A-A715-2754C6FA6C5D}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Ordering.Api\", \"Services\\Ordering\\Ordering.Api\\Ordering.Api.csproj\", \"{F26D75D9-B050-4F78-8911-65780DF78BA5}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Ordering.Domain\", \"Services\\Ordering\\Ordering.Domain\\Ordering.Domain.csproj\", \"{334B2EC6-F84D-47F2-83C3-7F4FECAA1163}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Ordering.Application\", \"Services\\Ordering\\Ordering.Application\\Ordering.Application.csproj\", \"{5547B022-4512-4765-A762-121A77F9D204}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Ordering.Infrastructure\", \"Services\\Ordering\\Ordering.Infrastructure\\Ordering.Infrastructure.csproj\", \"{884BB166-77C2-4B71-9E97-9739616120B4}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"BuildingBlocks\", \"BuildingBlocks\", \"{697BC681-3FB0-437B-9BB9-AF5BA4C929E0}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"EventBus.Messages\", \"BuildingBlocks\\EventBus.Messages\\EventBus.Messages.csproj\", \"{2318E480-B8A6-40F4-84E4-55A8162A4969}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"ApiGateways\", \"ApiGateways\", \"{9B4D3E8A-2363-4DB9-8B9A-CC7803EE3F86}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"OcelotApiGateway\", \"ApiGateways\\OcelotApiGateway\\OcelotApiGateway.csproj\", \"{14C156A3-CDE8-4501-A61A-1FF511F6255E}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"OnlineShop.Aggregator\", \"ApiGateways\\OnlineShop.Aggregator\\OnlineShop.Aggregator\\OnlineShop.Aggregator.csproj\", \"{3C7D0E5D-90CE-469B-AC79-1B89EEEFE3B8}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{A6F2D049-4B6E-4805-889F-49D113927DA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{A6F2D049-4B6E-4805-889F-49D113927DA6}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{A6F2D049-4B6E-4805-889F-49D113927DA6}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{A6F2D049-4B6E-4805-889F-49D113927DA6}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{9ED1D254-4DE6-4229-B3B8-A60383414787}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{9ED1D254-4DE6-4229-B3B8-A60383414787}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{9ED1D254-4DE6-4229-B3B8-A60383414787}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{9ED1D254-4DE6-4229-B3B8-A60383414787}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{820840D7-0105-4611-B956-235D6EE15191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{820840D7-0105-4611-B956-235D6EE15191}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{820840D7-0105-4611-B956-235D6EE15191}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{820840D7-0105-4611-B956-235D6EE15191}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{C8BD72CC-5DDA-4A81-8A5B-FB411D1707ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{C8BD72CC-5DDA-4A81-8A5B-FB411D1707ED}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{C8BD72CC-5DDA-4A81-8A5B-FB411D1707ED}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{C8BD72CC-5DDA-4A81-8A5B-FB411D1707ED}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{DAD4D4A1-9AEB-46CF-A143-D19C1EB9C36D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{DAD4D4A1-9AEB-46CF-A143-D19C1EB9C36D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{DAD4D4A1-9AEB-46CF-A143-D19C1EB9C36D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{DAD4D4A1-9AEB-46CF-A143-D19C1EB9C36D}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{F26D75D9-B050-4F78-8911-65780DF78BA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F26D75D9-B050-4F78-8911-65780DF78BA5}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F26D75D9-B050-4F78-8911-65780DF78BA5}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F26D75D9-B050-4F78-8911-65780DF78BA5}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{334B2EC6-F84D-47F2-83C3-7F4FECAA1163}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{334B2EC6-F84D-47F2-83C3-7F4FECAA1163}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{334B2EC6-F84D-47F2-83C3-7F4FECAA1163}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{334B2EC6-F84D-47F2-83C3-7F4FECAA1163}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{5547B022-4512-4765-A762-121A77F9D204}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{5547B022-4512-4765-A762-121A77F9D204}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{5547B022-4512-4765-A762-121A77F9D204}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{5547B022-4512-4765-A762-121A77F9D204}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{884BB166-77C2-4B71-9E97-9739616120B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{884BB166-77C2-4B71-9E97-9739616120B4}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{884BB166-77C2-4B71-9E97-9739616120B4}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{884BB166-77C2-4B71-9E97-9739616120B4}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{2318E480-B8A6-40F4-84E4-55A8162A4969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{2318E480-B8A6-40F4-84E4-55A8162A4969}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{2318E480-B8A6-40F4-84E4-55A8162A4969}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{2318E480-B8A6-40F4-84E4-55A8162A4969}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{14C156A3-CDE8-4501-A61A-1FF511F6255E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{14C156A3-CDE8-4501-A61A-1FF511F6255E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{14C156A3-CDE8-4501-A61A-1FF511F6255E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{14C156A3-CDE8-4501-A61A-1FF511F6255E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{3C7D0E5D-90CE-469B-AC79-1B89EEEFE3B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{3C7D0E5D-90CE-469B-AC79-1B89EEEFE3B8}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{3C7D0E5D-90CE-469B-AC79-1B89EEEFE3B8}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{3C7D0E5D-90CE-469B-AC79-1B89EEEFE3B8}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{653AE14C-8DD7-4E9C-96B5-D6B99C1D0CF7} = {087760BA-AF35-40D8-8779-01B3C48D3EF4}\n\t\t{A6F2D049-4B6E-4805-889F-49D113927DA6} = {653AE14C-8DD7-4E9C-96B5-D6B99C1D0CF7}\n\t\t{9830C312-7C6A-4AAE-9700-AF8E2BCF942D} = {087760BA-AF35-40D8-8779-01B3C48D3EF4}\n\t\t{820840D7-0105-4611-B956-235D6EE15191} = {9830C312-7C6A-4AAE-9700-AF8E2BCF942D}\n\t\t{4166A6E7-178A-4FDE-B7BD-33C5833F9472} = {087760BA-AF35-40D8-8779-01B3C48D3EF4}\n\t\t{C8BD72CC-5DDA-4A81-8A5B-FB411D1707ED} = {4166A6E7-178A-4FDE-B7BD-33C5833F9472}\n\t\t{DAD4D4A1-9AEB-46CF-A143-D19C1EB9C36D} = {4166A6E7-178A-4FDE-B7BD-33C5833F9472}\n\t\t{69172560-B5A3-488A-A715-2754C6FA6C5D} = {087760BA-AF35-40D8-8779-01B3C48D3EF4}\n\t\t{F26D75D9-B050-4F78-8911-65780DF78BA5} = {69172560-B5A3-488A-A715-2754C6FA6C5D}\n\t\t{334B2EC6-F84D-47F2-83C3-7F4FECAA1163} = {69172560-B5A3-488A-A715-2754C6FA6C5D}\n\t\t{5547B022-4512-4765-A762-121A77F9D204} = {69172560-B5A3-488A-A715-2754C6FA6C5D}\n\t\t{884BB166-77C2-4B71-9E97-9739616120B4} = {69172560-B5A3-488A-A715-2754C6FA6C5D}\n\t\t{2318E480-B8A6-40F4-84E4-55A8162A4969} = {697BC681-3FB0-437B-9BB9-AF5BA4C929E0}\n\t\t{14C156A3-CDE8-4501-A61A-1FF511F6255E} = {9B4D3E8A-2363-4DB9-8B9A-CC7803EE3F86}\n\t\t{3C7D0E5D-90CE-469B-AC79-1B89EEEFE3B8} = {9B4D3E8A-2363-4DB9-8B9A-CC7803EE3F86}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {A359A298-1C98-4D56-886A-777076E176E4}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "README.md",
    "content": "# OnlineShop\n\n🚀 **OnlineShop** is a comprehensive and feature-rich template repository for building robust .NET 8 applications.\n\n---\n\n## ⭐ Star This Repository!\n\n---\n\n## 🌟 What’s Inside OnlineShop?\n\nThis repository is packed with:\n\n### 🌐 Services\n- ➡ **Basket**\n- ➡ **Catalog**\n- ➡ **Discount**\n- ➡ **Ordering**\n\n### 🛑 APIGateways & BuildingBlocks\n- ➡ **Clean Architecture** for scalable applications\n- ➡ **CQRS pattern** for separating read and write operations\n- ➡ **Unit of Work** & **Repository patterns**\n- ➡ **EF Core** & **Dapper** for data access\n\n### 🗄️ Database & Caching Support\n- ➡ **SQLServer**, **Postgres**, & **MongoDB**\n- ➡ **Redis** for high-performance caching\n\n### 🛠 Middleware & Error Handling\n- ➡ **BaseResult pattern** for uniform API responses\n- ➡ **RabbitMQ** for messaging and background jobs\n\n### 📊 Load Balancing & Aggregator\n- ➡ **YARP** for Load Balancing\n- ➡ **Ocelot** for API Gateway\n\n### 🚀 API & Authentication\n- ➡ **JWT tokens** & **OAuth** for secure authentication and authorization\n\n### 🐳 Docker & DevOps\n- ➡ **Docker** support for containerization\n- ➡ **pgAdmin** for database management\n- ➡ **Portainer** for easy Docker management\n\n### 📋 Swagger & API Management\n- ➡ Fully configured **Swagger** with security and examples\n\n### 📌 Additional Tools & Patterns\n- ➡ **Custom Exceptions** and **Pagination Handlers**\n- ➡ Best practices in **DDD** and **OOP**\n\n---\n\n## 🔗 Explore the Repository\n\nYou can find all these features and more in the **OnlineShop** repository on GitHub. Feel free to **explore**, **fork**, and **contribute**!\n\n👉 [OnlineShop on GitHub](https://lnkd.in/d9aruGDU)\n\n---\n\n## 🤝 Get Involved!\n\nContributions, feedback, and suggestions are highly welcome! Let’s collaborate to make **OnlineShop** even better.\n\n---\n\n## Stay Connected\n\n- **GitHub**: [BehzadDara](https://github.com/BehzadDara)\n- **LinkedIn**: [Behzad Dara](https://www.linkedin.com/in/behzaddara/)\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Basket.Api.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <InvariantGlobalization>true</InvariantGlobalization>\n    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>\n    <DockerfileContext>..\\..\\..</DockerfileContext>\n    <DockerComposeProjectPath>..\\..\\..\\docker-compose.dcproj</DockerComposeProjectPath>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"AutoMapper.Extensions.Microsoft.DependencyInjection\" Version=\"12.0.1\" />\n    <PackageReference Include=\"Grpc.AspNetCore\" Version=\"2.59.0\" />\n    <PackageReference Include=\"MassTransit\" Version=\"8.1.2\" />\n    <PackageReference Include=\"MassTransit.RabbitMQ\" Version=\"8.1.2\" />\n    <PackageReference Include=\"Microsoft.Extensions.Caching.StackExchangeRedis\" Version=\"8.0.0\" />\n    <PackageReference Include=\"Microsoft.VisualStudio.Azure.Containers.Tools.Targets\" Version=\"1.19.5\" />\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n    <PackageReference Include=\"Swashbuckle.AspNetCore\" Version=\"6.5.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\..\\BuildingBlocks\\EventBus.Messages\\EventBus.Messages.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Protobuf Include=\"..\\..\\Discount\\Discount.Grpc\\Protos\\discount.proto\" GrpcServices=\"Client\">\n      <Link>Protos\\discount.proto</Link>\n    </Protobuf>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Basket.Api.http",
    "content": "@Basket.Api_HostAddress = http://localhost:5033\n\nGET {{Basket.Api_HostAddress}}/weatherforecast/\nAccept: application/json\n\n###\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Controllers/OrderController.cs",
    "content": "﻿using AutoMapper;\nusing Basket.Api.Entities;\nusing Basket.Api.GrpcServices;\nusing Basket.Api.Repositories;\nusing EventBus.Messages.Events;\nusing MassTransit;\nusing MassTransit.Transports;\nusing Microsoft.AspNetCore.Mvc;\nusing System.Net;\n\nnamespace Basket.Api.Controllers\n{\n    [Route(\"api/v1/[controller]/[action]\")]\n    [ApiController]\n    public class OrderController(\n        IOrderRepository _orderRepository, \n        DiscountGrpcService _discountService,\n        IMapper _mapper,\n        IPublishEndpoint _publishEndpoint\n        ) : ControllerBase\n    {\n        [HttpGet(\"{userName}\")]\n        [ProducesResponseType(typeof(Order), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<Order>> GetByUserName(string userName)\n        {\n            var result = await _orderRepository.GetByUserName(userName);\n            return Ok(result);\n        }\n\n        [HttpPost]\n        [ProducesResponseType(typeof(Order), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<Order>> Update([FromBody] Order order)\n        {\n            foreach (var orderItem in order.Items)\n            {\n                var coupon = await _discountService.GetDiscount(orderItem.ProductName);\n                orderItem.Price -= coupon.Amount;\n            }\n\n            var result = await _orderRepository.Update(order);\n            return Ok(result);\n        }\n\n        [HttpDelete(\"{userName}\")]\n        [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)]\n        public async Task<IActionResult> Delete(string userName)\n        {\n            await _orderRepository.Delete(userName);\n            return Ok();\n        }\n\n        [HttpPost]\n        [ProducesResponseType((int)HttpStatusCode.Accepted)]\n        [ProducesResponseType((int)HttpStatusCode.BadRequest)]\n        public async Task<IActionResult> Checkout([FromBody] BasketCheckout basketCheckout)\n        {\n            var basket = await _orderRepository.GetByUserName(basketCheckout.UserName);\n            if (basket is null)\n            {\n                return BadRequest();\n            }\n\n            var eventMessage = _mapper.Map<BasketCheckoutEvent>(basketCheckout);\n            eventMessage.TotalPrice = basket.TotalPrice;\n\n            await _publishEndpoint.Publish(eventMessage);\n\n            await _orderRepository.Delete(basketCheckout.UserName);\n\n            return Accepted();\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Dockerfile",
    "content": "#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.\n\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nUSER app\nWORKDIR /app\nEXPOSE 8080\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nARG BUILD_CONFIGURATION=Release\nWORKDIR /src\nCOPY [\"Services/Basket/Basket.Api/Basket.Api.csproj\", \"Services/Basket/Basket.Api/\"]\nCOPY [\"BuildingBlocks/EventBus.Messages/EventBus.Messages.csproj\", \"BuildingBlocks/EventBus.Messages/\"]\nRUN dotnet restore \"./Services/Basket/Basket.Api/./Basket.Api.csproj\"\nCOPY . .\nWORKDIR \"/src/Services/Basket/Basket.Api\"\nRUN dotnet build \"./Basket.Api.csproj\" -c $BUILD_CONFIGURATION -o /app/build\n\nFROM build AS publish\nARG BUILD_CONFIGURATION=Release\nRUN dotnet publish \"./Basket.Api.csproj\" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"Basket.Api.dll\"]"
  },
  {
    "path": "Services/Basket/Basket.Api/Entities/BasketCheckout.cs",
    "content": "﻿namespace Basket.Api.Entities;\n\npublic class BasketCheckout\n{\n    public string UserName { get; set; } = string.Empty;\n    public decimal TotalPrice { get; set; }\n\n    public string FirstName { get; set; } = string.Empty;\n    public string LastName { get; set; } = string.Empty;\n    public string EmailAddress { get; set; } = string.Empty;\n    public string Country { get; set; } = string.Empty;\n    public string City { get; set; } = string.Empty;\n\n    public string BankName { get; set; } = string.Empty;\n    public string RefCode { get; set; } = string.Empty;\n    public int PaymentMethod { get; set; }\n}\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Entities/Order.cs",
    "content": "﻿namespace Basket.Api.Entities;\n\npublic class Order(string userName)\n{\n    public string UserName { get; set; } = userName;\n    public List<OrderItem> Items { get; set; } = [];\n    public decimal TotalPrice \n    { \n        get \n        {\n            return Items.Sum(x => x.Quantity * x.Price);\n        } \n    }\n}\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Entities/OrderItem.cs",
    "content": "﻿namespace Basket.Api.Entities;\n\npublic class OrderItem\n{\n    public int Quantity { get; set; }\n    public string Color { get; set; } = string.Empty;\n    public decimal Price { get; set; }\n    public string ProductId { get; set; } = string.Empty;\n    public string ProductName { get; set; } = string.Empty;\n}\n"
  },
  {
    "path": "Services/Basket/Basket.Api/GrpcServices/DiscountGrpcService.cs",
    "content": "﻿using Discount.Grpc.Protos;\n\nnamespace Basket.Api.GrpcServices;\n\npublic class DiscountGrpcService(DiscountProtoService.DiscountProtoServiceClient _discountProtoService)\n{\n    public async Task<CouponModel> GetDiscount(string ProductName)\n    {\n        var discountRequest = new GetDiscountRequest { ProductName = ProductName };\n        return await _discountProtoService.GetDiscountAsync(discountRequest);\n    }\n}\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Mapper/BasketProfile.cs",
    "content": "﻿using AutoMapper;\nusing Basket.Api.Entities;\nusing EventBus.Messages.Events;\n\nnamespace Basket.Api.Mapper;\n\npublic class BasketProfile : Profile\n{\n    public BasketProfile()\n    {\n        CreateMap<BasketCheckout, BasketCheckoutEvent>().ReverseMap();\n    }\n}\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Program.cs",
    "content": "using Basket.Api.GrpcServices;\nusing Basket.Api.Repositories;\nusing Discount.Grpc.Protos;\nusing MassTransit;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddStackExchangeRedisCache(options =>\n{\n    options.Configuration = builder.Configuration.GetValue<string>(\"CacheSettings:ConnectionString\");\n});\n\nbuilder.Services.AddControllers();\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nbuilder.Services.AddGrpcClient<DiscountProtoService.DiscountProtoServiceClient>(options =>\n{\n    options.Address = new Uri(builder.Configuration.GetValue<string>(\"GrpcSettings:DiscountUrl\") ?? \"\");\n});\nbuilder.Services.AddScoped<DiscountGrpcService>();\nbuilder.Services.AddScoped<IOrderRepository, OrderRepository>();\n\nbuilder.Services.AddMassTransit(config =>\n{\n    config.UsingRabbitMq((context, conf) =>\n    {\n        conf.Host(builder.Configuration.GetValue<string>(\"EventBusSettings:HostAddress\"));\n    });\n});\n\nbuilder.Services.AddAutoMapper(typeof(Program));\n\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.MapControllers();\n\napp.Run();"
  },
  {
    "path": "Services/Basket/Basket.Api/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Basket.Api\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"dotnetRunMessages\": true,\n      \"applicationUrl\": \"http://localhost:5001\"\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"Docker\": {\n      \"commandName\": \"Docker\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"{Scheme}://{ServiceHost}:{ServicePort}/swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_HTTP_PORTS\": \"8001\"\n      },\n      \"publishAllPorts\": true\n    }\n  },\n  \"$schema\": \"http://json.schemastore.org/launchsettings.json\",\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:19262\",\n      \"sslPort\": 0\n    }\n  }\n}"
  },
  {
    "path": "Services/Basket/Basket.Api/Repositories/IOrderRepository.cs",
    "content": "﻿using Basket.Api.Entities;\n\nnamespace Basket.Api.Repositories;\n\npublic interface IOrderRepository\n{\n    Task<Order> GetByUserName(string userName);\n    Task<Order> Update(Order order);\n    Task Delete(string userName);\n}\n"
  },
  {
    "path": "Services/Basket/Basket.Api/Repositories/OrderRepository.cs",
    "content": "﻿using Basket.Api.Entities;\nusing Microsoft.Extensions.Caching.Distributed;\nusing Newtonsoft.Json;\n\nnamespace Basket.Api.Repositories;\n\npublic class OrderRepository(IDistributedCache _redisCache) : IOrderRepository\n{\n    public async Task<Order> GetByUserName(string userName)\n    {\n        var result = await _redisCache.GetStringAsync(userName);\n        if (string.IsNullOrEmpty(result))\n        {\n            return new Order(userName);\n        }\n\n        var order = JsonConvert.DeserializeObject<Order>(result);\n        if (order is null)\n        {\n            return new Order(userName);\n        }\n\n        return order;\n    }\n\n    public async Task<Order> Update(Order order)\n    {\n        await _redisCache.SetStringAsync(order.UserName, JsonConvert.SerializeObject(order));\n        return await GetByUserName(order.UserName);\n    }\n    public async Task Delete(string userName)\n    {\n        await _redisCache.RemoveAsync(userName);\n    }\n}\n"
  },
  {
    "path": "Services/Basket/Basket.Api/appsettings.Development.json",
    "content": "{\n  \"CacheSettings\": {\n    \"ConnectionString\": \"localhost:6379\"\n  },\n  \"GrpcSettings\": {\n    \"DiscountUrl\": \"http://localhost:5003\"\n  },\n  \"EventBusSettings\": {\n    \"HostAddress\": \"amqp://guest:guest@localhost:5672\"\n  },\n    \"Logging\": {\n      \"LogLevel\": {\n        \"Default\": \"Information\",\n        \"Microsoft.AspNetCore\": \"Warning\"\n      }\n    }\n  }\n"
  },
  {
    "path": "Services/Basket/Basket.Api/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Catalog.Api.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <InvariantGlobalization>true</InvariantGlobalization>\n    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>\n    <DockerfileContext>..\\..\\..</DockerfileContext>\n    <DockerComposeProjectPath>..\\..\\..\\docker-compose.dcproj</DockerComposeProjectPath>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.VisualStudio.Azure.Containers.Tools.Targets\" Version=\"1.19.5\" />\n    <PackageReference Include=\"MongoDB.Driver\" Version=\"2.22.0\" />\n    <PackageReference Include=\"Swashbuckle.AspNetCore\" Version=\"6.5.0\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Catalog.Api.http",
    "content": "@Catalog.Api_HostAddress = http://localhost:5049\n\nGET {{Catalog.Api_HostAddress}}/weatherforecast/\nAccept: application/json\n\n###\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Controllers/ProductController.cs",
    "content": "﻿using Catalog.Api.Entities;\nusing Catalog.Api.Repositories;\nusing Microsoft.AspNetCore.Mvc;\nusing System.Net;\n\nnamespace Catalog.Api.Controllers\n{\n    [Route(\"api/v1/[controller]/[action]\")]\n    [ApiController]\n    public class ProductController(IProductRepository _productRepository, ILogger<ProductController> _logger) : ControllerBase\n    {\n        [HttpGet]\n        [ProducesResponseType(typeof(IEnumerable<Product>), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<IEnumerable<Product>>> GetAll()\n        {\n            var result = await _productRepository.GetAll();\n            return Ok(result);\n        }\n\n        [HttpGet(\"{id:length(24)}\")]\n        [ProducesResponseType((int)HttpStatusCode.NotFound)]\n        [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<Product>> GetById(string id)\n        {\n            var result = await _productRepository.GetById(id);\n            if (result is null)\n            {\n                _logger.LogError($\"product with id: {id} not found.\");\n                return NotFound();\n            }\n            return Ok(result);\n        }\n\n        [HttpGet(\"{name}\")]\n        [ProducesResponseType((int)HttpStatusCode.NotFound)]\n        [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<Product>> GetByName(string name)\n        {\n            var result = await _productRepository.GetByName(name);\n            if (result is null)\n            {\n                _logger.LogError($\"product with name: {name} not found.\");\n                return NotFound();\n            }\n            return Ok(result);\n        }\n\n        [HttpGet(\"{category}\")]\n        [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<IEnumerable<Product>>> GetByCategory(string category)\n        {\n            var result = await _productRepository.GetByCategory(category);\n            return Ok(result);\n        }\n\n        [HttpPost]\n        [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<IEnumerable<Product>>> Create([FromBody] Product product)\n        {\n            await _productRepository.Create(product);\n            return CreatedAtRoute(\"GetProduct\", new { id = product.Id}, product);\n        }\n\n        [HttpPut]\n        [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<IEnumerable<Product>>> Update([FromBody] Product product)\n        {\n            var result = await _productRepository.Update(product);\n            return Ok(result);\n        }\n\n        [HttpDelete(\"{id:length(24)}\")]\n        [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<IEnumerable<Product>>> Delete(string id)\n        {\n            var result = await _productRepository.Delete(id);\n            return Ok(result);\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Data/CatalogContext.cs",
    "content": "﻿using Catalog.Api.Entities;\nusing MongoDB.Driver;\n\nnamespace Catalog.Api.Data;\n\npublic class CatalogContext : ICatalogContext\n{\n    public CatalogContext(IConfiguration configuration)\n    {\n        var client = new MongoClient(configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n        var database = client.GetDatabase(configuration.GetValue<string>(\"DatabaseSettings:DatabaseName\"));\n        Products = database.GetCollection<Product>(configuration.GetValue<string>(\"DatabaseSettings:CollectionName\"));\n        CatalogContextSeed.SeedData(Products);\n    }\n\n    public IMongoCollection<Product> Products { get; }\n}\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Data/CatalogContextSeed.cs",
    "content": "﻿using Catalog.Api.Entities;\nusing MongoDB.Driver;\n\nnamespace Catalog.Api.Data;\n\npublic class CatalogContextSeed\n{\n    public static void SeedData(IMongoCollection<Product> productCollection)\n    {\n        var existProduct = productCollection.Find(x => true).Any();\n        if (!existProduct)\n        {\n            productCollection.InsertManyAsync(GetSeedData());\n        }\n    }\n\n    private static List<Product> GetSeedData()\n    {\n        return\n            [\n                new()\n                {\n                    Id = \"602d2149e773f2a3990b47f5\",\n                    Name = \"IPhone X\",\n                    Summary = \"This phone is the company's biggest change to its flagship smartphone in years. It includes a borderless.\",\n                    Description = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus.\",\n                    ImageFile = \"product-1.png\",\n                    Price = 950.00M,\n                    Category = \"Smart Phone\"\n                },\n                new()\n                {\n                    Id = \"602d2149e773f2a3990b47f6\",\n                    Name = \"Samsung 10\",\n                    Summary = \"This phone is the company's biggest change to its flagship smartphone in years. It includes a borderless.\",\n                    Description = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus.\",\n                    ImageFile = \"product-2.png\",\n                    Price = 840.00M,\n                    Category = \"Smart Phone\"\n                },\n                new()\n                {\n                    Id = \"602d2149e773f2a3990b47f7\",\n                    Name = \"Huawei Plus\",\n                    Summary = \"This phone is the company's biggest change to its flagship smartphone in years. It includes a borderless.\",\n                    Description = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus.\",\n                    ImageFile = \"product-3.png\",\n                    Price = 650.00M,\n                    Category = \"White Appliances\"\n                },\n                new()\n                {\n                    Id = \"602d2149e773f2a3990b47f8\",\n                    Name = \"Xiaomi Mi 9\",\n                    Summary = \"This phone is the company's biggest change to its flagship smartphone in years. It includes a borderless.\",\n                    Description = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus.\",\n                    ImageFile = \"product-4.png\",\n                    Price = 470.00M,\n                    Category = \"White Appliances\"\n                },\n                new()\n                {\n                    Id = \"602d2149e773f2a3990b47f9\",\n                    Name = \"HTC U11+ Plus\",\n                    Summary = \"This phone is the company's biggest change to its flagship smartphone in years. It includes a borderless.\",\n                    Description = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus.\",\n                    ImageFile = \"product-5.png\",\n                    Price = 380.00M,\n                    Category = \"Smart Phone\"\n                },\n                new()\n                {\n                    Id = \"602d2149e773f2a3990b47fa\",\n                    Name = \"LG G7 ThinQ\",\n                    Summary = \"This phone is the company's biggest change to its flagship smartphone in years. It includes a borderless.\",\n                    Description = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut, tenetur natus doloremque laborum quos iste ipsum rerum obcaecati impedit odit illo dolorum ab tempora nihil dicta earum fugiat. Temporibus, voluptatibus.\",\n                    ImageFile = \"product-6.png\",\n                    Price = 240.00M,\n                    Category = \"Home Kitchen\"\n                }\n            ];\n    }\n}\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Data/ICatalogContext.cs",
    "content": "﻿using Catalog.Api.Entities;\nusing MongoDB.Driver;\n\nnamespace Catalog.Api.Data;\n\npublic interface ICatalogContext\n{\n    IMongoCollection<Product> Products { get; }\n}\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Dockerfile",
    "content": "#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.\n\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nUSER app\nWORKDIR /app\nEXPOSE 8080\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nARG BUILD_CONFIGURATION=Release\nWORKDIR /src\nCOPY [\"Services/Catalog/Catalog.Api/Catalog.Api.csproj\", \"Services/Catalog/Catalog.Api/\"]\nRUN dotnet restore \"./Services/Catalog/Catalog.Api/./Catalog.Api.csproj\"\nCOPY . .\nWORKDIR \"/src/Services/Catalog/Catalog.Api\"\nRUN dotnet build \"./Catalog.Api.csproj\" -c $BUILD_CONFIGURATION -o /app/build\n\nFROM build AS publish\nARG BUILD_CONFIGURATION=Release\nRUN dotnet publish \"./Catalog.Api.csproj\" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"Catalog.Api.dll\"]"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Entities/Product.cs",
    "content": "﻿using MongoDB.Bson;\nusing MongoDB.Bson.Serialization.Attributes;\n\nnamespace Catalog.Api.Entities;\n\npublic class Product\n{\n    [BsonId]\n    [BsonRepresentation(BsonType.ObjectId)]\n    public string Id { get; set; } = string.Empty;\n    [BsonElement(\"Name\")]\n    public string Name { get; set; } = string.Empty;\n    public string Category { get; set; } = string.Empty;\n    public string Summary { get; set; } = string.Empty;\n    public string Description { get; set; } = string.Empty;\n    public string ImageFile { get; set; } = string.Empty;\n    public decimal Price { get; set; }\n}\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Products.Json",
    "content": "[\n  {\n    \"Name\": \"Asus Laptop\",\n    \"Category\": \"Computers\",\n    \"Summary\": \"Summary\",\n    \"Description\": \"Description\",\n    \"ImageFile\": \"ImageFile\",\n    \"Price\": \"100\"\n  },\n  {\n    \"Name\": \"HP Laptop\",\n    \"Category\": \"Computers\",\n    \"Summary\": \"Summary\",\n    \"Description\": \"Description\",\n    \"ImageFile\": \"ImageFile\",\n    \"Price\": \"200\"\n  }\n]"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Program.cs",
    "content": "using Catalog.Api.Data;\nusing Catalog.Api.Repositories;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllers();\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nbuilder.Services.AddScoped<ICatalogContext, CatalogContext>();\nbuilder.Services.AddScoped<IProductRepository, ProductRepository>();\n\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.MapControllers();\n\napp.Run();\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Catalog.Api\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"dotnetRunMessages\": true,\n      \"applicationUrl\": \"http://localhost:5000\"\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"Docker\": {\n      \"commandName\": \"Docker\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"{Scheme}://{ServiceHost}:{ServicePort}/swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_HTTP_PORTS\": \"8000\"\n      },\n      \"publishAllPorts\": true\n    }\n  },\n  \"$schema\": \"http://json.schemastore.org/launchsettings.json\",\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:57789\",\n      \"sslPort\": 0\n    }\n  }\n}"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Repositories/IProductRepository.cs",
    "content": "﻿using Catalog.Api.Entities;\n\nnamespace Catalog.Api.Repositories;\n\npublic interface IProductRepository\n{\n    Task<IEnumerable<Product>> GetAll();\n    Task<Product> GetById(string id);\n    Task<Product> GetByName(string name);\n    Task<IEnumerable<Product>> GetByCategory(string category);\n    Task Create(Product product);\n    Task<bool> Update(Product product);\n    Task<bool> Delete(string id);\n}\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/Repositories/ProductRepository.cs",
    "content": "﻿using Catalog.Api.Data;\nusing Catalog.Api.Entities;\nusing MongoDB.Driver;\n\nnamespace Catalog.Api.Repositories;\n\npublic class ProductRepository(ICatalogContext _catalogContext) : IProductRepository\n{\n    public async Task<IEnumerable<Product>> GetAll()\n    {\n        return await _catalogContext.Products.Find(x => true).ToListAsync();\n    }\n    public async Task<Product> GetById(string id)\n    {\n        return await _catalogContext.Products.Find(x => x.Id == id).FirstOrDefaultAsync();\n    }\n    public async Task<Product> GetByName(string name)\n    {\n        return await _catalogContext.Products.Find(x => x.Name == name).FirstOrDefaultAsync();\n    }\n    public async Task<IEnumerable<Product>> GetByCategory(string category)\n    {\n        return await _catalogContext.Products.Find(x => x.Category == category).ToListAsync();\n    }\n    public async Task Create(Product product)\n    {\n        await _catalogContext.Products.InsertOneAsync(product);\n    }\n    public async Task<bool> Update(Product product)\n    {\n        var result = await _catalogContext.Products.ReplaceOneAsync(x => x.Id == product.Id, product);\n        return result.IsAcknowledged && result.ModifiedCount > 0;\n    }\n    public async Task<bool> Delete(string id)\n    {\n        var result = await _catalogContext.Products.DeleteOneAsync(x => x.Id == id);\n        return result.IsAcknowledged && result.DeletedCount > 0;\n    }\n} \n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/appsettings.Development.json",
    "content": "{\n  \"DatabaseSettings\": {\n    \"ConnectionString\": \"localhost://catalogdb:27017\",\n    \"DatabaseName\": \"CatalogDB\",\n    \"CollectionName\": \"Products\"\n  },\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  }\n}\n"
  },
  {
    "path": "Services/Catalog/Catalog.Api/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Api/Controllers/CouponController.cs",
    "content": "﻿using Discount.Api.Entities;\nusing Discount.Api.Repositories;\nusing Microsoft.AspNetCore.Mvc;\nusing System.Net;\n\nnamespace Discount.Api.Controllers\n{\n    [Route(\"api/v1/[controller]/[action]\")]\n    [ApiController]\n    public class CouponController(ICouponRepository _couponRepository) : ControllerBase\n    {\n        [HttpGet(\"{productName}\")]\n        [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<Coupon>> GetByProductName(string productName)\n        {\n            var result = await _couponRepository.GetByProductName(productName);\n            return Ok(result);\n        }\n\n        [HttpPost]\n        [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<Coupon>> Create([FromBody] Coupon coupon)\n        {\n            await _couponRepository.Create(coupon);\n            return await GetByProductName(coupon.ProductName);\n        }\n\n        [HttpPut]\n        [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<Coupon>> Update([FromBody] Coupon coupon)\n        {\n            await _couponRepository.Update(coupon);\n            return await GetByProductName(coupon.ProductName);\n        }\n\n        [HttpDelete(\"{productName}\")]\n        [ProducesResponseType(typeof(bool), (int)HttpStatusCode.OK)]\n        public async Task<ActionResult<Coupon>> Delete(string productName)\n        {\n            var result = await _couponRepository.Delete(productName);\n            return Ok(result);\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Api/Discount.Api.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <InvariantGlobalization>true</InvariantGlobalization>\n    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>\n    <DockerfileContext>..\\..\\..</DockerfileContext>\n    <DockerComposeProjectPath>..\\..\\..\\docker-compose.dcproj</DockerComposeProjectPath>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Dapper\" Version=\"2.1.24\" />\n    <PackageReference Include=\"Microsoft.VisualStudio.Azure.Containers.Tools.Targets\" Version=\"1.19.5\" />\n    <PackageReference Include=\"Npgsql\" Version=\"8.0.1\" />\n    <PackageReference Include=\"Swashbuckle.AspNetCore\" Version=\"6.5.0\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Services/Discount/Discount.Api/Discount.Api.http",
    "content": "@Discount.Api_HostAddress = http://localhost:5297\n\nGET {{Discount.Api_HostAddress}}/weatherforecast/\nAccept: application/json\n\n###\n"
  },
  {
    "path": "Services/Discount/Discount.Api/Dockerfile",
    "content": "#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.\n\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nUSER app\nWORKDIR /app\nEXPOSE 8080\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nARG BUILD_CONFIGURATION=Release\nWORKDIR /src\nCOPY [\"Services/Discount/Discount.Api/Discount.Api.csproj\", \"Services/Discount/Discount.Api/\"]\nRUN dotnet restore \"./Services/Discount/Discount.Api/./Discount.Api.csproj\"\nCOPY . .\nWORKDIR \"/src/Services/Discount/Discount.Api\"\nRUN dotnet build \"./Discount.Api.csproj\" -c $BUILD_CONFIGURATION -o /app/build\n\nFROM build AS publish\nARG BUILD_CONFIGURATION=Release\nRUN dotnet publish \"./Discount.Api.csproj\" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"Discount.Api.dll\"]"
  },
  {
    "path": "Services/Discount/Discount.Api/Entities/Coupon.cs",
    "content": "﻿namespace Discount.Api.Entities;\n\npublic class Coupon\n{\n    public int Id { get; set; }\n    public string ProductName { get; set; } = string.Empty;\n    public string Description { get; set; } = string.Empty;\n    public int Amount { get; set; }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Api/Extensions/HostExtensions.cs",
    "content": "﻿using Npgsql;\n\nnamespace Discount.Api.Extensions;\n\npublic static class HostExtensions\n{\n    public static IHost MigrateDatabase<TContext>(this IHost host, int retry = 0)\n    {\n        using var scope = host.Services.CreateScope();\n        var services = scope.ServiceProvider;\n        var configuration = services.GetRequiredService<IConfiguration>();\n        var logger = services.GetRequiredService<ILogger<TContext>>();\n\n        try\n        {\n            logger.LogInformation(\"migrating postgresql database\");\n\n            using var connection = new NpgsqlConnection\n                (configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n            connection.Open();\n\n            using var command = new NpgsqlCommand\n            {\n                Connection = connection\n            };\n\n            command.CommandText = @\"create table if not exists Coupon \n                (Id serial primary key,\n                ProductName varchar(200) not null,\n                Description text,\n                Amount int)\";\n            command.ExecuteNonQuery();\n\n            /*command.CommandText = @\"insert into Coupon(ProductName, Description, Amount) values \n                ('IPhone X', 'iphone discount', 150),\n                ('Samsung 10', 'samsung discount', 150)\";\n            command.ExecuteNonQuery();*/\n\n            logger.LogInformation(\"migration has been completed!\");\n        }\n        catch (Exception ex)\n        {\n            logger.LogError($\"an error has been occured: {ex.Message}\");\n\n            if (retry < 50)\n            {\n                Thread.Sleep(2000);\n                host.MigrateDatabase<TContext>(retry + 1);\n            }\n        }\n\n        return host;\n    }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Api/Program.cs",
    "content": "using Discount.Api.Extensions;\nusing Discount.Api.Repositories;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllers();\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nbuilder.Services.AddScoped<ICouponRepository, CouponRepository>();\n\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.MapControllers();\n\napp.MigrateDatabase<Program>();\n\napp.Run();\n"
  },
  {
    "path": "Services/Discount/Discount.Api/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Discount.Api\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"dotnetRunMessages\": true,\n      \"applicationUrl\": \"http://localhost:5002\"\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"Docker\": {\n      \"commandName\": \"Docker\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"{Scheme}://{ServiceHost}:{ServicePort}/swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_HTTP_PORTS\": \"8080\"\n      },\n      \"publishAllPorts\": true\n    }\n  },\n  \"$schema\": \"http://json.schemastore.org/launchsettings.json\",\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:18137\",\n      \"sslPort\": 0\n    }\n  }\n}"
  },
  {
    "path": "Services/Discount/Discount.Api/Repositories/CouponRepository.cs",
    "content": "﻿using Dapper;\nusing Discount.Api.Entities;\nusing Npgsql;\n\nnamespace Discount.Api.Repositories;\n\npublic class CouponRepository(IConfiguration _configuration) : ICouponRepository\n{\n    public async Task<Coupon> GetByProductName(string productName)\n    {\n        using var connection = new NpgsqlConnection\n            (_configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n\n        var coupon = await connection.QueryFirstOrDefaultAsync<Coupon>\n            ($\"select * from Coupon where ProductName = '{productName}'\");\n        if (coupon is null)\n        {\n            return new Coupon\n            {\n                ProductName = productName,\n                Description = \"No discount\",\n                Amount = 0\n            };\n        }\n\n        return coupon;\n    }\n\n    public async Task<bool> Create(Coupon coupon)\n    {\n        using var connection = new NpgsqlConnection\n            (_configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n\n        var affected = await connection.ExecuteAsync\n            ($\"insert into Coupon(ProductName, Description, Amount) \" +\n            $\"values('{coupon.ProductName}', '{coupon.Description}', {coupon.Amount})\");\n\n        return affected > 0;\n    }\n\n    public async Task<bool> Update(Coupon coupon)\n    {\n        using var connection = new NpgsqlConnection\n            (_configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n\n        var affected = await connection.ExecuteAsync\n            ($\"update Coupon \" +\n            $\"set ProductName = '{coupon.ProductName}', Description = '{coupon.Description}', Amount = {coupon.Amount} \" +\n            $\"where Id = {coupon.Id}\");\n\n        return affected > 0;\n    }\n\n    public async Task<bool> Delete(string productName)\n    {\n        using var connection = new NpgsqlConnection\n            (_configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n\n        var affected = await connection.ExecuteAsync\n            ($\"delete from Coupon where ProductName = {productName}\");\n\n        return affected > 0;\n    }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Api/Repositories/ICouponRepository.cs",
    "content": "﻿using Discount.Api.Entities;\n\nnamespace Discount.Api.Repositories;\n\npublic interface ICouponRepository\n{\n    Task<Coupon> GetByProductName(string productName);\n    Task<bool> Create(Coupon coupon);\n    Task<bool> Update(Coupon coupon);\n    Task<bool> Delete(string productName);\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Api/appsettings.Development.json",
    "content": "{\n  \"DatabaseSettings\": {\n    \"ConnectionString\": \"Server=localhost;port=5432;Database=discountdb;User Id=admin;Password=admin1234;\"\n  },\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Api/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Discount.Grpc.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>\n    <DockerfileContext>..\\..\\..</DockerfileContext>\n    <DockerComposeProjectPath>..\\..\\..\\docker-compose.dcproj</DockerComposeProjectPath>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"AutoMapper.Extensions.Microsoft.DependencyInjection\" Version=\"12.0.1\" />\n    <PackageReference Include=\"Grpc.AspNetCore\" Version=\"2.59.0\" />\n\t  <PackageReference Include=\"Dapper\" Version=\"2.1.24\" />\n\t  <PackageReference Include=\"Microsoft.VisualStudio.Azure.Containers.Tools.Targets\" Version=\"1.19.5\" />\n\t  <PackageReference Include=\"Npgsql\" Version=\"8.0.1\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Protobuf Include=\"Protos\\discount.proto\" GrpcServices=\"Server\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Dockerfile",
    "content": "#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.\n\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nUSER app\nWORKDIR /app\nEXPOSE 8080\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nARG BUILD_CONFIGURATION=Release\nWORKDIR /src\nCOPY [\"Services/Discount/Discount.Grpc/Discount.Grpc.csproj\", \"Services/Discount/Discount.Grpc/\"]\nRUN dotnet restore \"./Services/Discount/Discount.Grpc/./Discount.Grpc.csproj\"\nCOPY . .\nWORKDIR \"/src/Services/Discount/Discount.Grpc\"\nRUN dotnet build \"./Discount.Grpc.csproj\" -c $BUILD_CONFIGURATION -o /app/build\n\nFROM build AS publish\nARG BUILD_CONFIGURATION=Release\nRUN dotnet publish \"./Discount.Grpc.csproj\" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"Discount.Grpc.dll\"]"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Entities/Coupon.cs",
    "content": "﻿namespace Discount.Grpc.Entities;\n\npublic class Coupon\n{\n    public int Id { get; set; }\n    public string ProductName { get; set; } = string.Empty;\n    public string Description { get; set; } = string.Empty;\n    public int Amount { get; set; }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Extensions/HostExtensions.cs",
    "content": "﻿using Npgsql;\n\nnamespace Discount.Grpc.Extensions;\n\npublic static class HostExtensions\n{\n    public static IHost MigrateDatabase<TContext>(this IHost host, int retry = 0)\n    {\n        using var scope = host.Services.CreateScope();\n        var services = scope.ServiceProvider;\n        var configuration = services.GetRequiredService<IConfiguration>();\n        var logger = services.GetRequiredService<ILogger<TContext>>();\n\n        try\n        {\n            logger.LogInformation(\"migrating postgresql database\");\n\n            using var connection = new NpgsqlConnection\n                (configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n            connection.Open();\n\n            using var command = new NpgsqlCommand\n            {\n                Connection = connection\n            };\n\n            command.CommandText = @\"create table if not exists Coupon \n                (Id serial primary key,\n                ProductName varchar(200) not null,\n                Description text,\n                Amount int)\";\n            command.ExecuteNonQuery();\n\n            /*command.CommandText = @\"insert into Coupon(ProductName, Description, Amount) values \n                ('IPhone X', 'iphone discount', 150),\n                ('Samsung 10', 'samsung discount', 150)\";\n            command.ExecuteNonQuery();*/\n\n            logger.LogInformation(\"migration has been completed!\");\n        }\n        catch (Exception ex)\n        {\n            logger.LogError($\"an error has been occured: {ex.Message}\");\n\n            if (retry < 50)\n            {\n                Thread.Sleep(2000);\n                host.MigrateDatabase<TContext>(retry + 1);\n            }\n        }\n\n        return host;\n    }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Mapper/DiscountProfile.cs",
    "content": "﻿using AutoMapper;\nusing Discount.Grpc.Entities;\nusing Discount.Grpc.Protos;\n\nnamespace Discount.Grpc.Mapper;\n\npublic class DiscountProfile : Profile\n{\n    public DiscountProfile()\n    {\n        CreateMap<Coupon, CouponModel>().ReverseMap();\n    }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Program.cs",
    "content": "using Discount.Grpc.Extensions;\nusing Discount.Grpc.Repositories;\nusing Discount.Grpc.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddGrpc();\nbuilder.Services.AddAutoMapper(typeof(Program));\nbuilder.Services.AddScoped<ICouponRepository, CouponRepository>();\n\nvar app = builder.Build();\n\napp.MapGrpcService<DiscountService>();\n\napp.MigrateDatabase<Program>();\n\napp.Run();\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Discount.Grpc\": {\n      \"commandName\": \"Project\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"dotnetRunMessages\": true,\n      \"applicationUrl\": \"http://localhost:5003\"\n    },\n    \"Docker\": {\n      \"commandName\": \"Docker\",\n      \"launchUrl\": \"{Scheme}://{ServiceHost}:{ServicePort}\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_HTTP_PORTS\": \"8003\"\n      },\n      \"publishAllPorts\": true\n    }\n  },\n  \"$schema\": \"http://json.schemastore.org/launchsettings.json\"\n}"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Protos/discount.proto",
    "content": "syntax = \"proto3\";\n\noption csharp_namespace = \"Discount.Grpc.Protos\";\n\nservice DiscountProtoService {\n  rpc GetDiscount (GetDiscountRequest) returns (CouponModel);\n  rpc CreateDiscount (CreateDiscountRequest) returns (CouponModel);\n  rpc UpdateDiscount (UpdateDiscountRequest) returns (CouponModel);\n  rpc DeleteDiscount (DeleteDiscountRequest) returns (DeleteDiscountResponse);\n}\n\nmessage CouponModel {\n\tint32 Id = 1;\n\tstring ProductName = 2;\n\tstring Description = 3;\n\tint32 Amount = 4;\n}\n\nmessage GetDiscountRequest {\n  string ProductName = 1;\n}\n\nmessage CreateDiscountRequest {\n  CouponModel Coupon = 1;\n}\n\nmessage UpdateDiscountRequest {\n  CouponModel Coupon = 1;\n}\n\nmessage DeleteDiscountRequest {\n  string ProductName = 1;\n}\n\nmessage DeleteDiscountResponse {\n  bool Success = 1;\n}"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Repositories/CouponRepository.cs",
    "content": "﻿using Dapper;\nusing Discount.Grpc.Entities;\nusing Npgsql;\n\nnamespace Discount.Grpc.Repositories;\n\npublic class CouponRepository(IConfiguration _configuration) : ICouponRepository\n{\n    public async Task<Coupon> GetByProductName(string productName)\n    {\n        using var connection = new NpgsqlConnection\n            (_configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n\n        var coupon = await connection.QueryFirstOrDefaultAsync<Coupon>\n            ($\"select * from Coupon where ProductName = '{productName}'\");\n        if (coupon is null)\n        {\n            return new Coupon\n            {\n                ProductName = productName,\n                Description = \"No discount\",\n                Amount = 0\n            };\n        }\n\n        return coupon;\n    }\n\n    public async Task<bool> Create(Coupon coupon)\n    {\n        using var connection = new NpgsqlConnection\n            (_configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n\n        var affected = await connection.ExecuteAsync\n            ($\"insert into Coupon(ProductName, Description, Amount) \" +\n            $\"values('{coupon.ProductName}', '{coupon.Description}', {coupon.Amount})\");\n\n        return affected > 0;\n    }\n\n    public async Task<bool> Update(Coupon coupon)\n    {\n        using var connection = new NpgsqlConnection\n            (_configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n\n        var affected = await connection.ExecuteAsync\n            ($\"update Coupon \" +\n            $\"set ProductName = '{coupon.ProductName}', Description = '{coupon.Description}', Amount = {coupon.Amount} \" +\n            $\"where Id = {coupon.Id}\");\n\n        return affected > 0;\n    }\n\n    public async Task<bool> Delete(string productName)\n    {\n        using var connection = new NpgsqlConnection\n            (_configuration.GetValue<string>(\"DatabaseSettings:ConnectionString\"));\n\n        var affected = await connection.ExecuteAsync\n            ($\"delete from Coupon where ProductName = {productName}\");\n\n        return affected > 0;\n    }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Repositories/ICouponRepository.cs",
    "content": "﻿using Discount.Grpc.Entities;\n\nnamespace Discount.Grpc.Repositories;\n\npublic interface ICouponRepository\n{\n    Task<Coupon> GetByProductName(string productName);\n    Task<bool> Create(Coupon coupon);\n    Task<bool> Update(Coupon coupon);\n    Task<bool> Delete(string productName);\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/Services/DiscountService.cs",
    "content": "﻿using AutoMapper;\nusing Discount.Grpc.Entities;\nusing Discount.Grpc.Protos;\nusing Discount.Grpc.Repositories;\nusing Grpc.Core;\n\nnamespace Discount.Grpc.Services;\n\npublic class DiscountService(ICouponRepository _couponRepository, IMapper _mapper, ILogger<DiscountService> _logger) : DiscountProtoService.DiscountProtoServiceBase\n{\n    public async override Task<CouponModel> GetDiscount(GetDiscountRequest request, ServerCallContext context)\n    {\n        var coupon = await _couponRepository.GetByProductName(request.ProductName);\n\n        _logger.LogInformation($\"Get discount with product name {request.ProductName}\");\n\n        return _mapper.Map<CouponModel>(coupon);\n    }\n\n    public async override Task<CouponModel> CreateDiscount(CreateDiscountRequest request, ServerCallContext context)\n    {\n        var coupon = _mapper.Map<Coupon>(request.Coupon);\n        await _couponRepository.Create(coupon);\n\n        _logger.LogInformation($\"Create discount with product name {coupon.ProductName}\");\n\n        return _mapper.Map<CouponModel>(coupon);\n    }\n\n    public async override Task<CouponModel> UpdateDiscount(UpdateDiscountRequest request, ServerCallContext context)\n    {\n        var coupon = _mapper.Map<Coupon>(request.Coupon);\n        await _couponRepository.Update(coupon);\n\n        _logger.LogInformation($\"Update discount with product name {coupon.ProductName}\");\n\n        return _mapper.Map<CouponModel>(coupon);\n    }\n\n    public async override Task<DeleteDiscountResponse> DeleteDiscount(DeleteDiscountRequest request, ServerCallContext context)\n    {\n        var result = await _couponRepository.Delete(request.ProductName);\n\n        _logger.LogInformation($\"Delete discount with product name {request.ProductName}\");\n\n        return new DeleteDiscountResponse\n        {\n            Success = result,\n        };\n    }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/appsettings.Development.json",
    "content": "{\n  \"DatabaseSettings\": {\n    \"ConnectionString\": \"Server=localhost;port=5432;Database=discountdb;User Id=admin;Password=admin1234;\"\n  },\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  }\n}\n"
  },
  {
    "path": "Services/Discount/Discount.Grpc/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\",\n  \"Kestrel\": {\n    \"EndpointDefaults\": {\n      \"Protocols\": \"Http2\"\n    }\n  }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/Controllers/OrderController.cs",
    "content": "﻿using MediatR;\nusing Microsoft.AspNetCore.Mvc;\nusing Ordering.Application.Features.Orders.Commands.CreateOrder;\nusing Ordering.Application.Features.Orders.Commands.DeleteOrder;\nusing Ordering.Application.Features.Orders.Commands.UpdateOrder;\nusing Ordering.Application.Features.Orders.Queries.GetOrdersList;\nusing System.Net;\n\nnamespace Ordering.Api.Controllers;\n\n[ApiController]\n[Route(\"api/v1/[controller]/[action]\")]\npublic class OrderController(IMediator _mediator) : ControllerBase\n{\n    [HttpGet(\"{userName}\")]\n    [ProducesResponseType(typeof(IEnumerable<OrderViewModel>), (int)HttpStatusCode.OK)]\n    public async Task<ActionResult<IEnumerable<OrderViewModel>>> GetOrdersByUserName(string userName)\n    {\n        var result = await _mediator.Send(new GetOrdersListQuery(userName));\n        return Ok(result);\n    }\n\n    [HttpPost]\n    [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]\n    public async Task<ActionResult<int>> CreateOrder([FromBody] CreateOrderCommand command)\n    {\n        var result = await _mediator.Send(command);\n        return Ok(result);\n    }\n\n    [HttpPut]\n    [ProducesResponseType(typeof(int), (int)HttpStatusCode.NoContent)]\n    [ProducesResponseType(typeof(int), (int)HttpStatusCode.NotFound)]\n    public async Task<ActionResult> UpdateOrder([FromBody] UpdateOrderCommand command)\n    {\n        await _mediator.Send(command);\n        return NoContent();\n    }\n\n    [HttpDelete(\"{id}\")]\n    [ProducesResponseType(typeof(int), (int)HttpStatusCode.NoContent)]\n    [ProducesResponseType(typeof(int), (int)HttpStatusCode.NotFound)]\n    public async Task<ActionResult> DeleteOrder(int id)\n    {\n        await _mediator.Send(new DeleteOrderCommand(id));\n        return NoContent();\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/Dockerfile",
    "content": "#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.\n\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nUSER app\nWORKDIR /app\nEXPOSE 8080\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nARG BUILD_CONFIGURATION=Release\nWORKDIR /src\nCOPY [\"Services/Ordering/Ordering.Api/Ordering.Api.csproj\", \"Services/Ordering/Ordering.Api/\"]\nCOPY [\"BuildingBlocks/EventBus.Messages/EventBus.Messages.csproj\", \"BuildingBlocks/EventBus.Messages/\"]\nCOPY [\"Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj\", \"Services/Ordering/Ordering.Infrastructure/\"]\nCOPY [\"Services/Ordering/Ordering.Application/Ordering.Application.csproj\", \"Services/Ordering/Ordering.Application/\"]\nCOPY [\"Services/Ordering/Ordering.Domain/Ordering.Domain.csproj\", \"Services/Ordering/Ordering.Domain/\"]\nRUN dotnet restore \"./Services/Ordering/Ordering.Api/./Ordering.Api.csproj\"\nCOPY . .\nWORKDIR \"/src/Services/Ordering/Ordering.Api\"\nRUN dotnet build \"./Ordering.Api.csproj\" -c $BUILD_CONFIGURATION -o /app/build\n\nFROM build AS publish\nARG BUILD_CONFIGURATION=Release\nRUN dotnet publish \"./Ordering.Api.csproj\" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"Ordering.Api.dll\"]"
  },
  {
    "path": "Services/Ordering/Ordering.Api/EventBusConsumer/BasketCheckoutConsumer.cs",
    "content": "﻿using AutoMapper;\nusing EventBus.Messages.Events;\nusing MassTransit;\nusing MediatR;\nusing Ordering.Application.Features.Orders.Commands.CreateOrder;\n\nnamespace Ordering.Api.EventBusConsumer;\n\npublic class BasketCheckoutConsumer(\n    IMapper _mapper, \n    IMediator _mediator, \n    ILogger<BasketCheckoutConsumer> _logger\n    ) : IConsumer<BasketCheckoutEvent>\n{\n    public async Task Consume(ConsumeContext<BasketCheckoutEvent> context)\n    {\n        var createOrderCommand = _mapper.Map<CreateOrderCommand>(context.Message);\n        var result = await _mediator.Send(createOrderCommand);\n        _logger.LogInformation($\"Checkout basket consumed by Order service with result Id = {result}\");\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/HostExtensions.cs",
    "content": "﻿using Microsoft.EntityFrameworkCore;\n\nnamespace Ordering.Api;\n\npublic static class HostExtensions\n{\n    public static IHost MigrateDatabase<TContext>(this IHost host,\n        Action<TContext, IServiceProvider> seeder,\n        int? retry = 0) \n        where TContext : DbContext\n    {\n        var retryForAvailability = retry!.Value;\n\n        using var scope = host.Services.CreateScope();\n        var services = scope.ServiceProvider;\n        var logger = services.GetRequiredService<ILogger<TContext>>();\n        var context = services.GetService<TContext>();\n\n        try\n        {\n            logger.LogInformation(\"migrating started for sql server\");\n            InvokeSeeder(seeder, context, services);\n            logger.LogInformation(\"migrating finished for sql server\");\n        }\n        catch (Exception ex)\n        {\n            logger.LogError($\"migrating errored for sql server, {ex.Message}\");\n            if (retryForAvailability < 50)\n            {\n                Thread.Sleep(2000);\n                MigrateDatabase(host, seeder, retryForAvailability + 1);\n            }\n            throw;\n        }\n\n        return host;\n    }\n\n    private static void InvokeSeeder<TContext>(\n        Action<TContext, IServiceProvider> seeder,\n        TContext context, \n        IServiceProvider services) \n        where TContext : DbContext\n    {\n        context.Database.Migrate();\n        seeder(context, services);\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/Mapper/CheckoutProfile.cs",
    "content": "﻿using AutoMapper;\nusing EventBus.Messages.Events;\nusing Ordering.Application.Features.Orders.Commands.CreateOrder;\n\nnamespace Ordering.Api.Mapping;\n\npublic class CheckoutProfile : Profile\n{\n    public CheckoutProfile()\n    {\n        CreateMap<BasketCheckoutEvent, CreateOrderCommand>().ReverseMap();\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/Ordering.Api.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <InvariantGlobalization>false</InvariantGlobalization>\n    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>\n    <DockerfileContext>..\\..\\..</DockerfileContext>\n    <DockerComposeProjectPath>..\\..\\..\\docker-compose.dcproj</DockerComposeProjectPath>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"AutoMapper.Extensions.Microsoft.DependencyInjection\" Version=\"12.0.1\" />\n    <PackageReference Include=\"MassTransit\" Version=\"8.1.2\" />\n    <PackageReference Include=\"MassTransit.RabbitMQ\" Version=\"8.1.2\" />\n\t<PackageReference Include=\"Microsoft.EntityFrameworkCore.Design\" Version=\"8.0.0\" />\n\t<PackageReference Include=\"Microsoft.VisualStudio.Azure.Containers.Tools.Targets\" Version=\"1.19.5\" />\n    <PackageReference Include=\"Swashbuckle.AspNetCore\" Version=\"6.5.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\..\\BuildingBlocks\\EventBus.Messages\\EventBus.Messages.csproj\" />\n    <ProjectReference Include=\"..\\Ordering.Infrastructure\\Ordering.Infrastructure.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/Ordering.Api.http",
    "content": "@Ordering.Api_HostAddress = http://localhost:5116\n\nGET {{Ordering.Api_HostAddress}}/weatherforecast/\nAccept: application/json\n\n###\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/Program.cs",
    "content": "using EventBus.Messages.Common;\nusing MassTransit;\nusing Ordering.Api;\nusing Ordering.Api.EventBusConsumer;\nusing Ordering.Application;\nusing Ordering.Infrastructure.Persistence;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddControllers();\n\nbuilder.Services.AddApplicationServices();\nbuilder.Services.AddInfrastructureServices(builder.Configuration);\n\nbuilder.Services.AddMassTransit(config =>\n{\n    config.AddConsumer<BasketCheckoutConsumer>();\n\n    config.UsingRabbitMq((context, conf) =>\n    {\n        conf.Host(builder.Configuration.GetValue<string>(\"EventBusSettings:HostAddress\"));\n        conf.ReceiveEndpoint(EventBusConstant.BasketCheckoutQueue, c =>\n        {\n            c.ConfigureConsumer<BasketCheckoutConsumer>(context);\n        });\n    });\n});\n\nbuilder.Services.AddAutoMapper(typeof(Program));\nbuilder.Services.AddScoped<BasketCheckoutConsumer>();\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.MapControllers();\n\napp.MigrateDatabase<OrderDBContext>((context, services) =>\n{\n    var logger = services.GetService<ILogger<OrderDBContextSeed>>();\n    OrderDBContextSeed.SeedAsync(context, logger).Wait();\n});\n\napp.Run();\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Ordering.Api\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"dotnetRunMessages\": true,\n      \"applicationUrl\": \"http://localhost:5004\"\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"Docker\": {\n      \"commandName\": \"Docker\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"{Scheme}://{ServiceHost}:{ServicePort}/swagger\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_HTTP_PORTS\": \"8004\"\n      },\n      \"publishAllPorts\": true\n    }\n  },\n  \"$schema\": \"http://json.schemastore.org/launchsettings.json\",\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:54058\",\n      \"sslPort\": 0\n    }\n  }\n}"
  },
  {
    "path": "Services/Ordering/Ordering.Api/appsettings.Development.json",
    "content": "{\n  \"ConnectionStrings\": {\n    \"OrderingConnectionString\": \"Server=localhost; Database=orderdb; User Id=sa; Password=Admin1234; TrustServerCertificate=True;\"\n  },\n  \"EventBusSettings\": {\n    \"HostAddress\": \"amqp://guest:guest@localhost:5672\"\n  },\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Api/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/ApplicationServiceRegistration.cs",
    "content": "﻿using FluentValidation;\nusing MediatR;\nusing Microsoft.Extensions.DependencyInjection;\nusing Ordering.Application.Behaviors;\nusing System.Reflection;\n\nnamespace Ordering.Application;\n\npublic static class ApplicationServiceRegistration\n{\n    public static IServiceCollection AddApplicationServices(this IServiceCollection services)\n    {\n        services.AddAutoMapper(Assembly.GetExecutingAssembly());\n        services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());\n        services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));\n\n        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehavior<,>));\n        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));\n\n        return services;\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Behaviors/UnhandledExceptionBehavior.cs",
    "content": "﻿using FluentValidation;\nusing MediatR;\nusing Microsoft.Extensions.Logging;\n\nnamespace Ordering.Application.Behaviors;\n\npublic class UnhandledExceptionBehavior<TRequest, TResponse>(ILogger<TRequest> _logger)\n    : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>\n{\n    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)\n    {\n        try\n        {\n            return await next();\n        }\n        catch (Exception ex)\n        {\n            var requestName = typeof(TRequest).Name;\n            _logger.LogError(ex, $\"Application unhandled exception for request {requestName}\");\n            throw;\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Behaviors/ValidationBehavior.cs",
    "content": "﻿using FluentValidation;\nusing MediatR;\n\n\nnamespace Ordering.Application.Behaviors;\n\npublic class ValidationBehavior<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> _validators) \n    : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>\n{\n    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)\n    {\n        if (_validators.Any())\n        {\n            var content = new ValidationContext<TRequest>(request);\n\n            var validationResults = await Task.WhenAll(\n                _validators.Select(x => x.ValidateAsync(content, cancellationToken)));\n\n            var failures = validationResults.SelectMany(x => x.Errors).Where(x => x != null).ToList();\n            if (failures.Count != 0)\n            {\n                throw new Exceptions.ValidationException(failures);\n            }\n        }\n\n        return await next();\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Contracts/Infrastructure/IEmailService.cs",
    "content": "﻿using Ordering.Application.Models;\n\nnamespace Ordering.Application.Contracts.Infrastructure;\n\npublic interface IEmailService\n{\n    Task<bool> SendEmailAsync(Email email);\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Contracts/Persistence/IAsyncRepository.cs",
    "content": "﻿using Ordering.Domain.Common;\nusing System.Linq.Expressions;\n\nnamespace Ordering.Application.Contracts.Persistence;\n\npublic interface IAsyncRepository<T> where T : EntityBase\n{\n    Task<IReadOnlyList<T>> GetAllAsync();\n    Task<IReadOnlyList<T>> GetAsync(Expression<Func<T, bool>> predicate);\n    Task<IReadOnlyList<T>> GetAsync(Expression<Func<T, bool>>? predicate = null,\n        Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null,\n        string? includeString = null,\n        bool disableTracking = true);\n    Task<IReadOnlyList<T>> GetAsync(Expression<Func<T, bool>>? predicate = null,\n        Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null,\n        List<Expression<Func<T, object>>>? includes = null,\n        bool disableTracking = true);\n    Task<T?> GetByIdAsync(int id);\n    Task<T> CreateAsync(T entity);\n    Task UpdateAsync(T entity);\n    Task DeleteAsync(T entity);\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Contracts/Persistence/IOrderRepository.cs",
    "content": "﻿using Ordering.Domain.Entities;\n\nnamespace Ordering.Application.Contracts.Persistence;\n\npublic interface IOrderRepository : IAsyncRepository<Order>\n{\n    Task<IEnumerable<Order>> GetOrdersByUserName(string userName);\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Exceptions/NotFoundException.cs",
    "content": "﻿namespace Ordering.Application.Exceptions;\n\npublic class NotFoundException(string name, object key) \n    : ApplicationException($\"Entity with name \\\"{name}\\\" and key \\\"{key}\\\" was not found\")\n{\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Exceptions/ValidationException.cs",
    "content": "﻿using FluentValidation.Results;\n\nnamespace Ordering.Application.Exceptions;\n\npublic class ValidationException : ApplicationException\n{\n    public ValidationException()\n        : base(\"one or more validation errors has occured\")\n    {\n        \n    }\n\n    public ValidationException(IEnumerable<ValidationFailure> failures) : this()\n    {\n        Errors = failures.GroupBy(x => x.PropertyName, x => x.ErrorMessage)\n            .ToDictionary(x => x.Key, x => x.ToList());\n    }\n\n    public IDictionary<string, List<string>> Errors { get; set; } = new Dictionary<string, List<string>>();\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Commands/CreateOrder/CreateOrderCommand.cs",
    "content": "﻿using MediatR;\n\nnamespace Ordering.Application.Features.Orders.Commands.CreateOrder;\n\npublic sealed record CreateOrderCommand(\n        string UserName,\n        decimal TotalPrice,\n        string FirstName,\n        string LastName,\n        string EmailAddress,\n        string Country,\n        string City,\n        string BankName,\n        string RefCode,\n        int PaymentMethod\n    ) : IRequest<int>;"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Commands/CreateOrder/CreateOrderCommandHandler.cs",
    "content": "﻿using AutoMapper;\nusing MediatR;\nusing Microsoft.Extensions.Logging;\nusing Ordering.Application.Contracts.Infrastructure;\nusing Ordering.Application.Contracts.Persistence;\nusing Ordering.Application.Models;\nusing Ordering.Domain.Entities;\n\nnamespace Ordering.Application.Features.Orders.Commands.CreateOrder;\n\npublic class CreateOrderCommandHandler(IOrderRepository _orderRepository,\n                                       IMapper _mapper,\n                                       IEmailService _emailService,\n                                       ILogger<CreateOrderCommandHandler> _logger) : IRequestHandler<CreateOrderCommand, int>\n{\n    public async Task<int> Handle(CreateOrderCommand request, CancellationToken cancellationToken)\n    {\n        var entity = _mapper.Map<Order>(request);\n        var result = await _orderRepository.CreateAsync(entity);\n\n        _logger.LogInformation($\"order by Id {result.Id} created\");\n        await SendMail(result);\n\n        return result.Id;\n    }\n\n    private async Task SendMail(Order order)\n    {\n        try\n        {\n            await _emailService.SendEmailAsync(new Email\n            {\n                To = \"Test@Test.com\",\n                Subject = \"order added\",\n                Body = \"email Body\"\n            });\n        }\n        catch (Exception ex)\n        {\n            _logger.LogError($\"email has not been sent, {ex.Message}\");\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Commands/CreateOrder/CreateOrderCommandValidator.cs",
    "content": "﻿using FluentValidation;\n\nnamespace Ordering.Application.Features.Orders.Commands.CreateOrder;\n\npublic class CreateOrderCommandValidator : AbstractValidator<CreateOrderCommand>\n{\n    public CreateOrderCommandValidator()\n    {\n        RuleFor(x => x.UserName)\n            .NotNull().WithMessage(\"UserName is required\")\n            .NotEmpty().WithMessage(\"UserName can not be empty\")\n            .MinimumLength(3).WithMessage(\"UserName must be longer\");\n\n        RuleFor(x => x.EmailAddress)\n            .NotEmpty().WithMessage(\"Email is required\");\n\n        RuleFor(x => x.TotalPrice)\n            .NotEmpty().WithMessage(\"TotalPrice is required\")\n            .GreaterThan(0).WithMessage(\"TotalPrice must be greater than zero\");\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Commands/DeleteOrder/DeleteOrderCommand.cs",
    "content": "﻿using MediatR;\n\nnamespace Ordering.Application.Features.Orders.Commands.DeleteOrder;\n\npublic sealed record DeleteOrderCommand(int Id) : IRequest;"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Commands/DeleteOrder/DeleteOrderCommandHandler.cs",
    "content": "﻿using MediatR;\nusing Microsoft.Extensions.Logging;\nusing Ordering.Application.Contracts.Persistence;\nusing Ordering.Application.Exceptions;\nusing Ordering.Domain.Entities;\n\nnamespace Ordering.Application.Features.Orders.Commands.DeleteOrder\n{\n    public class DeleteOrderCommandHandler(IOrderRepository _orderRepository,\n                                           ILogger<DeleteOrderCommandHandler> _logger) : IRequestHandler<DeleteOrderCommand>\n    {\n        public async Task Handle(DeleteOrderCommand request, CancellationToken cancellationToken)\n        {\n            var order = await _orderRepository.GetByIdAsync(request.Id);\n            if (order is null)\n            {\n                _logger.LogError($\"order with id {request.Id} does not exist\");\n                throw new NotFoundException(nameof(Order), request.Id);\n            }\n\n            await _orderRepository.DeleteAsync(order);\n\n            _logger.LogInformation($\"order by Id {order.Id} deleted\");\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Commands/UpdateOrder/UpdateOrderCommand.cs",
    "content": "﻿using MediatR;\n\nnamespace Ordering.Application.Features.Orders.Commands.UpdateOrder;\n\npublic sealed record UpdateOrderCommand(\n        int Id,\n        string UserName,\n        decimal TotalPrice,\n        string FirstName,\n        string LastName,\n        string EmailAddress,\n        string Country,\n        string City,\n        string BankName,\n        string RefCode,\n        int PaymentMethod\n    ) : IRequest;"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Commands/UpdateOrder/UpdateOrderCommandHandler.cs",
    "content": "﻿using AutoMapper;\nusing MediatR;\nusing Microsoft.Extensions.Logging;\nusing Ordering.Application.Contracts.Persistence;\nusing Ordering.Application.Exceptions;\nusing Ordering.Domain.Entities;\n\nnamespace Ordering.Application.Features.Orders.Commands.UpdateOrder;\n\npublic class UpdateOrderCommandHandler(IOrderRepository _orderRepository,\n                                       IMapper _mapper,\n                                       ILogger<UpdateOrderCommandHandler> _logger) : IRequestHandler<UpdateOrderCommand>\n{\n    public async Task Handle(UpdateOrderCommand request, CancellationToken cancellationToken)\n    {\n        var order = await _orderRepository.GetByIdAsync(request.Id);\n        if (order is null)\n        {\n            _logger.LogError($\"order with id {request.Id} does not exist\");\n            throw new NotFoundException(nameof(Order), request.Id);\n        }\n\n        _mapper.Map(request, order);\n\n        await _orderRepository.UpdateAsync(order);\n\n        _logger.LogInformation($\"order by Id {order.Id} updated\");\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Commands/UpdateOrder/UpdateOrderCommandValidator.cs",
    "content": "﻿using FluentValidation;\n\nnamespace Ordering.Application.Features.Orders.Commands.UpdateOrder;\n\npublic class UpdateOrderCommandValidator : AbstractValidator<UpdateOrderCommand>\n{\n    public UpdateOrderCommandValidator()\n    {\n        RuleFor(x => x.UserName)\n            .NotNull().WithMessage(\"UserName is required\")\n            .NotEmpty().WithMessage(\"UserName can not be empty\")\n            .MinimumLength(3).WithMessage(\"UserName must be longer\");\n\n        RuleFor(x => x.EmailAddress)\n            .NotEmpty().WithMessage(\"Email is required\");\n\n        RuleFor(x => x.TotalPrice)\n            .NotEmpty().WithMessage(\"TotalPrice is required\")\n            .GreaterThan(0).WithMessage(\"TotalPrice must be greater than zero\");\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Queries/GetOrdersList/GetOrdersListQuery.cs",
    "content": "﻿using MediatR;\n\nnamespace Ordering.Application.Features.Orders.Queries.GetOrdersList;\n\npublic sealed record GetOrdersListQuery(string UserName) : IRequest<List<OrderViewModel>>;\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Queries/GetOrdersList/GetOrdersListQueryHandler.cs",
    "content": "﻿using AutoMapper;\nusing MediatR;\nusing Ordering.Application.Contracts.Persistence;\n\nnamespace Ordering.Application.Features.Orders.Queries.GetOrdersList;\n\npublic class GetOrdersListQueryHandler(IOrderRepository _orderRepository, IMapper _mapper) : IRequestHandler<GetOrdersListQuery, List<OrderViewModel>>\n{\n    public async Task<List<OrderViewModel>> Handle(GetOrdersListQuery request, CancellationToken cancellationToken)\n    {\n        var result = await _orderRepository.GetOrdersByUserName(request.UserName);\n        return _mapper.Map<List<OrderViewModel>>(result);\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Features/Orders/Queries/GetOrdersList/OrderViewModel.cs",
    "content": "﻿namespace Ordering.Application.Features.Orders.Queries.GetOrdersList;\n\npublic class OrderViewModel\n{\n    public int Id { get; set; }\n    public string UserName { get; set; } = string.Empty;\n    public decimal TotalPrice { get; set; }\n\n    public string FirstName { get; set; } = string.Empty;\n    public string LastName { get; set; } = string.Empty;\n    public string EmailAddress { get; set; } = string.Empty;\n    public string Country { get; set; } = string.Empty;\n    public string City { get; set; } = string.Empty;\n\n    public string BankName { get; set; } = string.Empty;\n    public string RefCode { get; set; } = string.Empty;\n    public int PaymentMethod { get; set; }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Mappings/MappingProfile.cs",
    "content": "﻿using AutoMapper;\nusing Ordering.Application.Features.Orders.Commands.CreateOrder;\nusing Ordering.Application.Features.Orders.Commands.UpdateOrder;\nusing Ordering.Application.Features.Orders.Queries.GetOrdersList;\nusing Ordering.Domain.Entities;\n\nnamespace Ordering.Application.Mappings;\n\npublic class MappingProfile : Profile\n{\n    public MappingProfile()\n    {\n        CreateMap<Order, OrderViewModel>().ReverseMap();\n        CreateMap<CreateOrderCommand, Order>().ReverseMap();\n        CreateMap<UpdateOrderCommand, Order>().ReverseMap();\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Models/Email.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Ordering.Application.Models;\n\npublic class Email\n{\n    public string To { get; set; } = string.Empty;\n    public string Subject { get; set; } = string.Empty;\n    public string Body { get; set; } = string.Empty;\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Models/EmailSetting.cs",
    "content": "﻿namespace Ordering.Application.Models;\n\npublic class EmailSetting\n{\n    public string From { get; set; } = string.Empty;\n    public string DisplayName { get; set; } = string.Empty;\n    public string UserName { get; set; } = string.Empty;\n    public string Password { get; set; } = string.Empty;\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Application/Ordering.Application.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Ordering.Domain\\Ordering.Domain.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"AutoMapper\" Version=\"12.0.1\" />\n    <PackageReference Include=\"AutoMapper.Extensions.Microsoft.DependencyInjection\" Version=\"12.0.1\" />\n    <PackageReference Include=\"FluentValidation\" Version=\"11.8.1\" />\n    <PackageReference Include=\"FluentValidation.DependencyInjectionExtensions\" Version=\"11.8.1\" />\n    <PackageReference Include=\"MediatR\" Version=\"12.2.0\" />\n    <PackageReference Include=\"MediatR.Extensions.Microsoft.DependencyInjection\" Version=\"11.1.0\" />\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Abstractions\" Version=\"8.0.0\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Services/Ordering/Ordering.Domain/Common/EntityBase.cs",
    "content": "﻿namespace Ordering.Domain.Common;\n\npublic abstract class EntityBase\n{\n    public int Id { get; set; }\n    public string CreatedBy { get; set; } = string.Empty;\n    public DateTime CreatedAt { get; set; }\n    public string UpdatedBy { get; set; } = string.Empty;\n    public DateTime? UpdatedAt { get; set; }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Domain/Common/ValueObject.cs",
    "content": "﻿namespace Ordering.Domain.Common;\n\npublic abstract class ValueObject\n{\n    protected static bool EqualOperator(ValueObject left, ValueObject right)\n    {\n        if (left is null ^ right is null)\n        {\n            return false;\n        }\n        return ReferenceEquals(left, right) || left.Equals(right);\n    }\n\n    protected static bool NotEqualOperator(ValueObject left, ValueObject right)\n    {\n        return !(EqualOperator(left, right));\n    }\n\n    protected abstract IEnumerable<object> GetEqualityComponents();\n\n    public override bool Equals(object obj)\n    {\n        if (obj == null || obj.GetType() != GetType())\n        {\n            return false;\n        }\n\n        var other = (ValueObject)obj;\n\n        return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());\n    }\n\n    public override int GetHashCode()\n    {\n        return GetEqualityComponents()\n            .Select(x => x != null ? x.GetHashCode() : 0)\n            .Aggregate((x, y) => x ^ y);\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Domain/Entities/Order.cs",
    "content": "﻿using Ordering.Domain.Common;\n\nnamespace Ordering.Domain.Entities;\n\npublic class Order : EntityBase\n{\n    public string UserName { get; set; } = string.Empty;\n    public decimal TotalPrice { get; set; }\n\n    public string FirstName { get; set; } = string.Empty;\n    public string LastName { get; set; } = string.Empty;\n    public string EmailAddress { get; set; } = string.Empty;\n    public string Country { get; set; } = string.Empty;\n    public string City { get; set; } = string.Empty;\n\n    public string BankName { get; set; } = string.Empty;\n    public string RefCode { get; set; } = string.Empty;\n    public int PaymentMethod { get; set; }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/InfrastructureServiceRegistration.cs",
    "content": "﻿using Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Ordering.Application.Contracts.Infrastructure;\nusing Ordering.Application.Contracts.Persistence;\nusing Ordering.Infrastructure.Persistence;\nusing Ordering.Infrastructure.Proxies;\nusing Ordering.Infrastructure.Repositories;\n\nnamespace Ordering.Application;\n\npublic static class InfrastructureServiceRegistration\n{\n    public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, \n        IConfiguration configuration)\n    {\n        services.AddDbContext<OrderDBContext>(options =>\n        {\n            options.UseSqlServer(configuration.GetConnectionString(\"OrderingConnectionString\"), builder =>\n            {\n                builder.EnableRetryOnFailure(3, TimeSpan.FromSeconds(5), null);\n            });\n        });\n\n        services.AddScoped(typeof(IAsyncRepository<>), typeof(RepositoryBase<>));\n        services.AddScoped<IOrderRepository, OrderRepository>();\n        services.AddScoped<IEmailService, EmailService>();\n\n        return services;\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Migrations/20231211200458_Init.Designer.cs",
    "content": "﻿// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastructure;\nusing Microsoft.EntityFrameworkCore.Metadata;\nusing Microsoft.EntityFrameworkCore.Migrations;\nusing Microsoft.EntityFrameworkCore.Storage.ValueConversion;\nusing Ordering.Infrastructure.Persistence;\n\n#nullable disable\n\nnamespace Ordering.Infrastructure.Migrations\n{\n    [DbContext(typeof(OrderDBContext))]\n    [Migration(\"20231211200458_Init\")]\n    partial class Init\n    {\n        /// <inheritdoc />\n        protected override void BuildTargetModel(ModelBuilder modelBuilder)\n        {\n#pragma warning disable 612, 618\n            modelBuilder\n                .HasAnnotation(\"ProductVersion\", \"8.0.0\")\n                .HasAnnotation(\"Relational:MaxIdentifierLength\", 128);\n\n            SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);\n\n            modelBuilder.Entity(\"Ordering.Domain.Entities.Order\", b =>\n                {\n                    b.Property<int>(\"Id\")\n                        .ValueGeneratedOnAdd()\n                        .HasColumnType(\"int\");\n\n                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>(\"Id\"));\n\n                    b.Property<string>(\"BankName\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"City\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"Country\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<DateTime>(\"CreatedAt\")\n                        .HasColumnType(\"datetime2\");\n\n                    b.Property<string>(\"CreatedBy\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"EmailAddress\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"FirstName\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"LastName\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<int>(\"PaymentMethod\")\n                        .HasColumnType(\"int\");\n\n                    b.Property<string>(\"RefCode\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<decimal>(\"TotalPrice\")\n                        .HasColumnType(\"decimal(18,4)\");\n\n                    b.Property<DateTime?>(\"UpdatedAt\")\n                        .HasColumnType(\"datetime2\");\n\n                    b.Property<string>(\"UpdatedBy\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"UserName\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.HasKey(\"Id\");\n\n                    b.ToTable(\"Orders\");\n                });\n#pragma warning restore 612, 618\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Migrations/20231211200458_Init.cs",
    "content": "﻿using System;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ordering.Infrastructure.Migrations\n{\n    /// <inheritdoc />\n    public partial class Init : Migration\n    {\n        /// <inheritdoc />\n        protected override void Up(MigrationBuilder migrationBuilder)\n        {\n            migrationBuilder.CreateTable(\n                name: \"Orders\",\n                columns: table => new\n                {\n                    Id = table.Column<int>(type: \"int\", nullable: false)\n                        .Annotation(\"SqlServer:Identity\", \"1, 1\"),\n                    UserName = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    TotalPrice = table.Column<decimal>(type: \"decimal(18,4)\", nullable: false),\n                    FirstName = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    LastName = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    EmailAddress = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    Country = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    City = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    BankName = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    RefCode = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    PaymentMethod = table.Column<int>(type: \"int\", nullable: false),\n                    CreatedBy = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    CreatedAt = table.Column<DateTime>(type: \"datetime2\", nullable: false),\n                    UpdatedBy = table.Column<string>(type: \"nvarchar(max)\", nullable: false),\n                    UpdatedAt = table.Column<DateTime>(type: \"datetime2\", nullable: true)\n                },\n                constraints: table =>\n                {\n                    table.PrimaryKey(\"PK_Orders\", x => x.Id);\n                });\n        }\n\n        /// <inheritdoc />\n        protected override void Down(MigrationBuilder migrationBuilder)\n        {\n            migrationBuilder.DropTable(\n                name: \"Orders\");\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Migrations/OrderDBContextModelSnapshot.cs",
    "content": "﻿// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastructure;\nusing Microsoft.EntityFrameworkCore.Metadata;\nusing Microsoft.EntityFrameworkCore.Storage.ValueConversion;\nusing Ordering.Infrastructure.Persistence;\n\n#nullable disable\n\nnamespace Ordering.Infrastructure.Migrations\n{\n    [DbContext(typeof(OrderDBContext))]\n    partial class OrderDBContextModelSnapshot : ModelSnapshot\n    {\n        protected override void BuildModel(ModelBuilder modelBuilder)\n        {\n#pragma warning disable 612, 618\n            modelBuilder\n                .HasAnnotation(\"ProductVersion\", \"8.0.0\")\n                .HasAnnotation(\"Relational:MaxIdentifierLength\", 128);\n\n            SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);\n\n            modelBuilder.Entity(\"Ordering.Domain.Entities.Order\", b =>\n                {\n                    b.Property<int>(\"Id\")\n                        .ValueGeneratedOnAdd()\n                        .HasColumnType(\"int\");\n\n                    SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>(\"Id\"));\n\n                    b.Property<string>(\"BankName\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"City\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"Country\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<DateTime>(\"CreatedAt\")\n                        .HasColumnType(\"datetime2\");\n\n                    b.Property<string>(\"CreatedBy\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"EmailAddress\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"FirstName\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"LastName\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<int>(\"PaymentMethod\")\n                        .HasColumnType(\"int\");\n\n                    b.Property<string>(\"RefCode\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<decimal>(\"TotalPrice\")\n                        .HasColumnType(\"decimal(18,4)\");\n\n                    b.Property<DateTime?>(\"UpdatedAt\")\n                        .HasColumnType(\"datetime2\");\n\n                    b.Property<string>(\"UpdatedBy\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.Property<string>(\"UserName\")\n                        .IsRequired()\n                        .HasColumnType(\"nvarchar(max)\");\n\n                    b.HasKey(\"Id\");\n\n                    b.ToTable(\"Orders\");\n                });\n#pragma warning restore 612, 618\n        }\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.EntityFrameworkCore\" Version=\"8.0.0\" />\n\t<PackageReference Include=\"Microsoft.EntityFrameworkCore.SqlServer\" Version=\"8.0.0\" />\n\t<PackageReference Include=\"Microsoft.EntityFrameworkCore.Relational\" Version=\"8.0.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Ordering.Application\\Ordering.Application.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Persistence/OrderDBContext.cs",
    "content": "﻿using Microsoft.EntityFrameworkCore;\nusing Ordering.Domain.Common;\nusing Ordering.Domain.Entities;\nusing System.Reflection;\n\nnamespace Ordering.Infrastructure.Persistence;\n\npublic class OrderDBContext(DbContextOptions<OrderDBContext> options) : DbContext(options)\n{\n    public DbSet<Order> Orders { get; set; }\n\n    protected override void OnModelCreating(ModelBuilder modelBuilder)\n    {\n        modelBuilder.Entity<Order>()\n            .Property(x => x.TotalPrice)\n            .HasColumnType(\"decimal(18,4)\");\n    }\n\n    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)\n    {\n        foreach (var entry in ChangeTracker.Entries<EntityBase>())\n        {\n            switch (entry.State)\n            {\n                case EntityState.Added:\n                    entry.Entity.CreatedAt = DateTime.UtcNow;\n                    entry.Entity.CreatedBy = \"Admin1\";\n                    break;\n                case EntityState.Modified:\n                    entry.Entity.UpdatedAt = DateTime.UtcNow;\n                    entry.Entity.UpdatedBy = \"Admin2\";\n                    break;\n            }\n        }\n\n        return base.SaveChangesAsync(cancellationToken);\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Persistence/OrderDBContextSeed.cs",
    "content": "﻿using Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.Logging;\nusing Ordering.Domain.Entities;\n\nnamespace Ordering.Infrastructure.Persistence;\n\npublic class OrderDBContextSeed\n{\n    public static async Task SeedAsync(OrderDBContext orderDBContext, ILogger<OrderDBContextSeed> logger)\n    {\n        if (!await orderDBContext.Orders.AnyAsync())\n        {\n            await orderDBContext.Orders.AddRangeAsync(GetPreconfiguredOrders());\n            await orderDBContext.SaveChangesAsync();\n\n            logger.LogInformation(\"data seed section configured\");\n        }\n    }\n\n    private static IEnumerable<Order> GetPreconfiguredOrders()\n    {\n        return new List<Order>\n        {\n            new() {\n                UserName = \"U1\",\n                FirstName = \"F1\",\n                LastName = \"D1\",\n                EmailAddress = \"E1@e.com\",\n                Country = \"Co1\",\n                City = \"ci1\",\n                TotalPrice = 10000\n            },\n            new() {\n                UserName = \"U2\",\n                FirstName = \"F2\",\n                LastName = \"D2\",\n                EmailAddress = \"E2@e.com\",\n                Country = \"Co2\",\n                City = \"ci2\",\n                TotalPrice = 20000\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Proxies/EmailService.cs",
    "content": "﻿using Ordering.Application.Contracts.Infrastructure;\nusing Ordering.Application.Models;\n\nnamespace Ordering.Infrastructure.Proxies;\n\npublic class EmailService : IEmailService\n{\n    public Task<bool> SendEmailAsync(Email email)\n    {\n        return Task.FromResult(true);\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs",
    "content": "﻿using Microsoft.EntityFrameworkCore;\nusing Ordering.Application.Contracts.Persistence;\nusing Ordering.Domain.Entities;\nusing Ordering.Infrastructure.Persistence;\n\nnamespace Ordering.Infrastructure.Repositories;\n\npublic class OrderRepository(OrderDBContext orderDBContext) \n    : RepositoryBase<Order>(orderDBContext), IOrderRepository\n{\n    public async Task<IEnumerable<Order>> GetOrdersByUserName(string userName)\n    {\n        return await _dbSet.Where(x => x.UserName == userName).ToListAsync();\n    }\n}\n"
  },
  {
    "path": "Services/Ordering/Ordering.Infrastructure/Repositories/RepositoryBase.cs",
    "content": "﻿using Microsoft.EntityFrameworkCore;\nusing Ordering.Application.Contracts.Persistence;\nusing Ordering.Domain.Common;\nusing Ordering.Infrastructure.Persistence;\nusing System.Linq.Expressions;\n\nnamespace Ordering.Infrastructure.Repositories;\n\npublic class RepositoryBase<T>(OrderDBContext _orderDBContext) : IAsyncRepository<T> where T : EntityBase\n{\n    protected readonly DbSet<T> _dbSet = _orderDBContext.Set<T>();\n\n    public async Task<IReadOnlyList<T>> GetAllAsync()\n    {\n        return await _dbSet.ToListAsync();\n    }\n\n    public async Task<IReadOnlyList<T>> GetAsync(Expression<Func<T, bool>> predicate)\n    {\n        return await _dbSet.Where(predicate).ToListAsync();\n    }\n\n    public async Task<IReadOnlyList<T>> GetAsync(Expression<Func<T, bool>>? predicate = null, Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null, string? includeString = null, bool disableTracking = true)\n    {\n        IQueryable<T> query = _dbSet;\n\n        if (disableTracking)\n        {\n            query = query.AsNoTracking();   \n        }\n\n        if (!string.IsNullOrWhiteSpace(includeString))\n        {\n            query = query.Include(includeString);\n        }\n\n        if (predicate != null)\n        {\n            query = query.Where(predicate);\n        }\n\n        if (orderBy != null)\n        {\n            query = orderBy(query);\n        }\n\n        return await query.ToListAsync();\n    }\n\n    public async Task<IReadOnlyList<T>> GetAsync(Expression<Func<T, bool>>? predicate = null, Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null, List<Expression<Func<T, object>>>? includes = null, bool disableTracking = true)\n    {\n        IQueryable<T> query = _dbSet;\n\n        if (disableTracking)\n        {\n            query = query.AsNoTracking();\n        }\n\n        if (includes != null)\n        {\n            query = includes.Aggregate(query, (current, include) => current.Include(include));\n        }\n\n        if (predicate != null)\n        {\n            query = query.Where(predicate);\n        }\n\n        if (orderBy != null)\n        {\n            query = orderBy(query);\n        }\n\n        return await query.ToListAsync();\n    }\n\n    public virtual async Task<T?> GetByIdAsync(int id)\n    {\n        return await _dbSet.FindAsync(id);\n    }\n\n    public async Task<T> CreateAsync(T entity)\n    {\n        await _dbSet.AddAsync(entity);\n        await _orderDBContext.SaveChangesAsync();\n        return entity;\n    }\n\n    public async Task UpdateAsync(T entity)\n    {\n        _orderDBContext.Entry(entity).State = EntityState.Modified;\n        await _orderDBContext.SaveChangesAsync();\n    }\n\n    public async Task DeleteAsync(T entity)\n    {\n        _dbSet.Remove(entity);\n        await _orderDBContext.SaveChangesAsync();\n    }\n}\n"
  },
  {
    "path": "docker-compose.dcproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" Sdk=\"Microsoft.Docker.Sdk\">\n  <PropertyGroup Label=\"Globals\">\n    <ProjectVersion>2.1</ProjectVersion>\n    <DockerTargetOS>Linux</DockerTargetOS>\n    <DockerPublishLocally>False</DockerPublishLocally>\n    <ProjectGuid>9ed1d254-4de6-4229-b3b8-a60383414787</ProjectGuid>\n    <DockerLaunchAction>LaunchBrowser</DockerLaunchAction>\n    <DockerServiceUrl>{Scheme}://localhost:{ServicePort}/swagger</DockerServiceUrl>\n    <DockerServiceName>catalog.api</DockerServiceName>\n  </PropertyGroup>\n  <ItemGroup>\n    <None Remove=\".github\\**\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"docker-compose.override.yml\">\n      <DependentUpon>docker-compose.yml</DependentUpon>\n    </None>\n    <None Include=\"docker-compose.yml\" />\n    <None Include=\".dockerignore\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "docker-compose.override.yml",
    "content": "version: '3.4'\n\nservices:\n\n  catalogdb:\n    container_name: catalogdb\n    restart: always\n    volumes:\n      - ./mongo_data:/data/db\n    ports:\n      - 27017:27017\n\n  basketdb:\n    container_name: basketdb\n    restart: always\n    ports:\n      - 6379:6379\n\n  discountdb:\n    container_name: discountdb\n    environment:\n      - POSTGRES_USER=admin\n      - POSTGRES_PASSWORD=admin1234\n      - POSTGRES_DB=discountdb\n    restart: always\n    ports:\n      - 5432:5432\n    volumes:\n      - postgres_data:/var/lib/postgresql/data/\n\n  orderdb:\n    container_name: orderdb\n    environment:\n      - SA_PASSWORD=Admin1234\n      - ACCEPT_EULA=Y\n    restart: always\n    ports:\n      - 1433:1433\n\n  mongoclient:\n    container_name: mongoclient\n    restart: always\n    ports:\n      - 5040:3000\n\n  pgadmin:\n    container_name: pgadmin\n    environment:\n      - PGADMIN_DEFAULT_EMAIL=admin@aspnetrun.com\n      - PGADMIN_DEFAULT_PASSWORD=admin1234\n    restart: always\n    ports:\n      - 5050:80\n    volumes:\n      - pgadmin_data:/root/.pgadmin\n\n  portainer:\n    container_name: portainer\n    restart: always\n    ports:\n      - 9000:9000\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock\n      - portainer_data:/data\n\n  rabbitmq:\n    container_name: rabbitmq\n    restart: always\n    ports:\n      - 5672:5672\n      - 15672:15672\n\n  catalog.api:\n    container_name: catalog.api\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Development\n      - ASPNETCORE_HTTP_PORTS=8000\n      - DatabaseSettings:ConnectionString=mongodb://catalogdb:27017\n    depends_on:\n      - catalogdb\n    ports:\n      - 8000:8000\n\n  basket.api:\n    container_name: basket.api\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Development\n      - ASPNETCORE_HTTP_PORTS=8001\n      - CacheSettings:ConnectionString=basketdb:6379\n      - GrpcSettings:DiscountUrl=http://discount.grpc:8003\n      - EventBusSettings:HostAddress=amqp://guest:guest@rabbitmq:5672\n    depends_on:\n      - basketdb\n      - rabbitmq\n    ports:\n      - 8001:8001\n\n  discount.api:\n    container_name: discount.api\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Development\n      - ASPNETCORE_HTTP_PORTS=8002\n      - DatabaseSettings:ConnectionString=Server=discountdb;Port=5432;Database=discountdb;User Id=admin;Password=admin1234\n    depends_on:\n      - discountdb\n    ports:\n      - 8002:8002\n\n  discount.grpc:\n    container_name: discount.grpc\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Development\n      - ASPNETCORE_HTTP_PORTS=8003\n      - DatabaseSettings:ConnectionString=Server=discountdb;Port=5432;Database=discountdb;User Id=admin;Password=admin1234\n    ports:\n      - 8003:8003\n\n  ordering.api:\n    container_name: ordering.api\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Development\n      - ASPNETCORE_HTTP_PORTS=8004\n      - ConnectionStrings:OrderingConnectionString=Server=orderdb;Database=orderdb;User Id=sa;Password=Admin1234;TrustServerCertificate=True;\n      # Connect orderdb in SSMS using IpConfig -> Ethernet adapter vEthernet (WSL) -> Ip V4\n      - EventBusSettings:HostAddress=amqp://guest:guest@rabbitmq:5672\n    depends_on:\n      - orderdb\n      - rabbitmq\n    ports:\n      - 8004:8004\n\n  ocelotapigateway:\n    container_name: ocelotapigateway\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Development\n      - ASPNETCORE_HTTP_PORTS=8010\n    depends_on:\n      - catalog.api\n      - basket.api\n      - discount.api\n      - ordering.api\n    ports:\n      - 8010:8010\n\n  onlineshop.aggregator:\n    container_name: onlineshop.aggregator\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Development\n      - ASPNETCORE_HTTP_PORTS=8005\n      - ApiSettings:CatalogUrl=http://catalog.api:8000\n      - ApiSettings:BasketUrl=http://basket.api:8001\n      - ApiSettings:OrderingUrl=http://ordering.api:8004\n    depends_on:\n      - catalog.api\n      - basket.api\n      - ordering.api\n    ports:\n      - 8005:8005\n\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3.4'\n\nservices:\n  catalogdb:\n    image: mongo\n\n  basketdb:\n    image: redis:alpine\n\n  discountdb:\n    image: postgres\n\n  orderdb:\n    image: mcr.microsoft.com/mssql/server\n\n  mongoclient:\n    image: mongoclient/mongoclient\n\n  pgadmin:\n    image: dpage/pgadmin4\n    \n  portainer:\n    image: portainer/portainer-ce\n\n  rabbitmq:\n    image: rabbitmq:3-management-alpine\n\n  catalog.api:\n    image: ${DOCKER_REGISTRY-}catalogapi\n    build:\n      context: .\n      dockerfile: Services/Catalog/Catalog.Api/Dockerfile\n\n  basket.api:\n    image: ${DOCKER_REGISTRY-}basketapi\n    build:\n      context: .\n      dockerfile: Services/Basket/Basket.Api/Dockerfile\n\n  discount.api:\n    image: ${DOCKER_REGISTRY-}discountapi\n    build:\n      context: .\n      dockerfile: Services/Discount/Discount.Api/Dockerfile\n\n  discount.grpc:\n    image: ${DOCKER_REGISTRY-}discountgrpc\n    build:\n      context: .\n      dockerfile: Services/Discount/Discount.Grpc/Dockerfile\n\n  ordering.api:\n    image: ${DOCKER_REGISTRY-}orderingapi\n    build:\n      context: .\n      dockerfile: Services/Ordering/Ordering.Api/Dockerfile\n\n  ocelotapigateway:\n    image: ${DOCKER_REGISTRY-}ocelotapigateway\n    build:\n      context: .\n      dockerfile: ApiGateways/OcelotApiGateway/Dockerfile\n\n  onlineshop.aggregator:\n    image: ${DOCKER_REGISTRY-}onlineshopaggregator\n    build:\n      context: .\n      dockerfile: ApiGateways/OnlineShop.Aggregator/OnlineShop.Aggregator/Dockerfile\n\nvolumes:\n  portainer_data:\n  postgres_data:\n  pgadmin_data:\n\n\n"
  },
  {
    "path": "launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Docker Compose\": {\n      \"commandName\": \"DockerCompose\",\n      \"commandVersion\": \"1.0\",\n      \"serviceActions\": {\n        \"catalog.api\": \"StartDebugging\",\n        \"basket.api\": \"StartDebugging\",\n        \"discount.api\": \"StartDebugging\",\n        \"discount.grpc\": \"StartDebugging\",\n        \"ordering.api\": \"StartDebugging\",\n        \"ocelotapigateway\": \"StartDebugging\",\n        \"onlineshop.aggregator\": \"StartDebugging\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "mongo_data/WiredTiger",
    "content": "WiredTiger\nWiredTiger 11.2.0: (November 10, 2022)\n"
  },
  {
    "path": "mongo_data/WiredTiger.turtle",
    "content": "WiredTiger version string\nWiredTiger 11.2.0: (November 10, 2022)\nWiredTiger version\nmajor=11,minor=2,patch=0\nfile:WiredTiger.wt\naccess_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.1611=(addr=\"018081e4ae54ed9b8181e46b1ff6018281e4991a01b5808080e2afc0e23fc0\",order=1611,time=1702853401,size=28672,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=146,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=4768,run_write_gen=4377)),checkpoint_backup_info=,checkpoint_lsn=(75,120320)\n"
  }
]