From e99b2c5a5afd28f487d64a8d152d5247177b7d92 Mon Sep 17 00:00:00 2001 From: Ignacio Gomez Date: Sun, 22 Jun 2025 19:34:07 -0600 Subject: [PATCH] First version of Service --- .gitignore | 363 ++++++++++++++++++ .../Core.Inventory.Application.csproj | 17 + .../Inventory/Adapter/FurnitureBasePort.cs | 33 ++ .../Inventory/Adapter/FurnitureVariantPort.cs | 37 ++ .../Input/ChangeFurnitureBaseStatusRequest.cs | 25 ++ .../ChangeFurnitureVariantStatusRequest.cs | 24 ++ .../Inventory/Input/Common/Dimensions.cs | 18 + .../Input/CreateFurnitureBaseRequest.cs | 33 ++ .../Input/CreateFurnitureVariantRequest.cs | 37 ++ .../Input/GetAllFurnitureBaseRequest.cs | 14 + ...GetAllFurnitureVariantsByModelIdRequest.cs | 19 + .../Input/GetFurnitureBaseByIdRequest.cs | 22 ++ .../Input/GetFurnitureVariantByIdRequest.cs | 19 + .../Input/UpdateFurnitureBaseRequest.cs | 35 ++ .../Input/UpdateFurnitureVariantRequest.cs | 41 ++ .../UseCases/Inventory/InventoryHandler.cs | 324 ++++++++++++++++ .../Inventory/Ports/IFurnitureBasePort.cs | 22 ++ .../Inventory/Ports/IFurnitureVariantPort.cs | 22 ++ .../ChangeFurnitureBaseStatusValidator.cs | 17 + .../ChangeFurnitureVariantStatusValidator.cs | 17 + .../Validator/Common/DimensionsValidator.cs | 20 + .../Validator/CreateFurnitureBaseValidator.cs | 29 ++ .../CreateFurnitureVariantValidator.cs | 35 ++ ...tAllFurnitureVariantsByModelIdValidator.cs | 14 + .../GetFurnitureBaseByIdValidator.cs | 14 + .../GetFurnitureVariantByIdValidator.cs | 14 + .../Validator/UpdateFurnitureBaseValidator.cs | 24 ++ .../UpdateFurnitureVariantValidator.cs | 38 ++ .../RegisterClientConfiguration.cs | 58 +++ .../Clients/IInventoryServiceClient.cs | 49 +++ .../Clients/Requests/FurnitureBaseRequest.cs | 17 + .../Requests/FurnitureVariantRequest.cs | 19 + .../Core.Inventory.External.csproj | 15 + .../GatewayConfiguration.cs | 20 + .../GatewaySettingsConfigurations.cs | 52 +++ .../Helpers/AuthenticatedHttpClientHandler.cs | 26 ++ .../Helpers/HttpContextTokenProvider.cs | 25 ++ .../Helpers/ITokenProvider.cs | 13 + .../Controllers/FurnitureBaseController.cs | 77 ++++ .../Controllers/FurnitureVariantController.cs | 70 ++++ .../Core.Inventory.Service.API.csproj | 20 + .../Core.Inventory.Service.API.http | 6 + .../Extensions/ServiceCollectionExtension.cs | 65 ++++ .../Extensions/SwaggerExtensions.cs | 98 +++++ Core.Inventory.Service.API/Program.cs | 80 ++++ .../Properties/launchSettings.json | 41 ++ Core.Inventory.Service.API/WeatherForecast.cs | 13 + .../appsettings.Development.json | 9 + .../appsettings.Local.json | 12 + Core.Inventory.Service.API/appsettings.json | 9 + Core.Inventory.Service.sln | 46 +++ 51 files changed, 2167 insertions(+) create mode 100644 .gitignore create mode 100644 Core.Inventory.Application/Core.Inventory.Application.csproj create mode 100644 Core.Inventory.Application/UseCases/Inventory/Adapter/FurnitureBasePort.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Adapter/FurnitureVariantPort.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/ChangeFurnitureBaseStatusRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/ChangeFurnitureVariantStatusRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/Common/Dimensions.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/CreateFurnitureBaseRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/CreateFurnitureVariantRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/GetAllFurnitureBaseRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/GetAllFurnitureVariantsByModelIdRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/GetFurnitureBaseByIdRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/GetFurnitureVariantByIdRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/UpdateFurnitureBaseRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Input/UpdateFurnitureVariantRequest.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/InventoryHandler.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Ports/IFurnitureBasePort.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Ports/IFurnitureVariantPort.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/ChangeFurnitureBaseStatusValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/ChangeFurnitureVariantStatusValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/Common/DimensionsValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/CreateFurnitureBaseValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/CreateFurnitureVariantValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/GetAllFurnitureVariantsByModelIdValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/GetFurnitureBaseByIdValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/GetFurnitureVariantByIdValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/UpdateFurnitureBaseValidator.cs create mode 100644 Core.Inventory.Application/UseCases/Inventory/Validator/UpdateFurnitureVariantValidator.cs create mode 100644 Core.Inventory.External/ClientConfiguration/RegisterClientConfiguration.cs create mode 100644 Core.Inventory.External/Clients/IInventoryServiceClient.cs create mode 100644 Core.Inventory.External/Clients/Requests/FurnitureBaseRequest.cs create mode 100644 Core.Inventory.External/Clients/Requests/FurnitureVariantRequest.cs create mode 100644 Core.Inventory.External/Core.Inventory.External.csproj create mode 100644 Core.Inventory.External/GatewayConfigurations/GatewayConfiguration.cs create mode 100644 Core.Inventory.External/GatewayConfigurations/GatewaySettingsConfigurations.cs create mode 100644 Core.Inventory.External/Helpers/AuthenticatedHttpClientHandler.cs create mode 100644 Core.Inventory.External/Helpers/HttpContextTokenProvider.cs create mode 100644 Core.Inventory.External/Helpers/ITokenProvider.cs create mode 100644 Core.Inventory.Service.API/Controllers/FurnitureBaseController.cs create mode 100644 Core.Inventory.Service.API/Controllers/FurnitureVariantController.cs create mode 100644 Core.Inventory.Service.API/Core.Inventory.Service.API.csproj create mode 100644 Core.Inventory.Service.API/Core.Inventory.Service.API.http create mode 100644 Core.Inventory.Service.API/Extensions/ServiceCollectionExtension.cs create mode 100644 Core.Inventory.Service.API/Extensions/SwaggerExtensions.cs create mode 100644 Core.Inventory.Service.API/Program.cs create mode 100644 Core.Inventory.Service.API/Properties/launchSettings.json create mode 100644 Core.Inventory.Service.API/WeatherForecast.cs create mode 100644 Core.Inventory.Service.API/appsettings.Development.json create mode 100644 Core.Inventory.Service.API/appsettings.Local.json create mode 100644 Core.Inventory.Service.API/appsettings.json create mode 100644 Core.Inventory.Service.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/Core.Inventory.Application/Core.Inventory.Application.csproj b/Core.Inventory.Application/Core.Inventory.Application.csproj new file mode 100644 index 0000000..a901630 --- /dev/null +++ b/Core.Inventory.Application/Core.Inventory.Application.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/Core.Inventory.Application/UseCases/Inventory/Adapter/FurnitureBasePort.cs b/Core.Inventory.Application/UseCases/Inventory/Adapter/FurnitureBasePort.cs new file mode 100644 index 0000000..489be72 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Adapter/FurnitureBasePort.cs @@ -0,0 +1,33 @@ +// *********************************************************************** +// +// Core.Inventory.Application +// +// *********************************************************************** +using Core.Adapters.Lib; +using Core.Inventory.Application.UseCases.Inventory.Ports; +using Lib.Architecture.BuildingBlocks; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Inventory.Application.UseCases.Inventory.Adapter +{ + public class FurnitureBasePort : BasePresenter, IFurnitureBasePort + { + /// + /// Handles success with a single . + /// + /// The output adapter. + public void Success(FurnitureBase output) + { + ViewModel = new OkObjectResult(output); + } + + /// + /// Handles success with a list of . + /// + /// The output list. + public void Success(List output) + { + ViewModel = new OkObjectResult(output); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Adapter/FurnitureVariantPort.cs b/Core.Inventory.Application/UseCases/Inventory/Adapter/FurnitureVariantPort.cs new file mode 100644 index 0000000..689f8b0 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Adapter/FurnitureVariantPort.cs @@ -0,0 +1,37 @@ +// *********************************************************************** +// +// Core.Inventory.Application +// +// *********************************************************************** + +using Core.Adapters.Lib; +using Core.Inventory.Application.UseCases.Inventory.Ports; +using Lib.Architecture.BuildingBlocks; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Inventory.Application.UseCases.Inventory.Adapter +{ + /// + /// Presenter for FurnitureVariant use cases. Maps output to HTTP responses. + /// + public class FurnitureVariantPort : BasePresenter, IFurnitureVariantPort + { + /// + /// Handles success with a single . + /// + /// The output adapter. + public void Success(FurnitureVariant output) + { + ViewModel = new OkObjectResult(output); + } + + /// + /// Handles success with a list of . + /// + /// The output list. + public void Success(List output) + { + ViewModel = new OkObjectResult(output); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/ChangeFurnitureBaseStatusRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/ChangeFurnitureBaseStatusRequest.cs new file mode 100644 index 0000000..56b60c2 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/ChangeFurnitureBaseStatusRequest.cs @@ -0,0 +1,25 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** + +using Core.Blueprint.Mongo; +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + /// + /// Command to change the status of a furniture base model. + /// + public class ChangeFurnitureBaseStatusRequest : Notificator, ICommand + { + public string Id { get; set; } = null!; + public StatusEnum Status { get; set; } + + public bool Validate() + { + return !string.IsNullOrWhiteSpace(Id); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/ChangeFurnitureVariantStatusRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/ChangeFurnitureVariantStatusRequest.cs new file mode 100644 index 0000000..b8c28b9 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/ChangeFurnitureVariantStatusRequest.cs @@ -0,0 +1,24 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Core.Blueprint.Mongo; +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + /// + /// Command to change the status of a furniture variant. + /// + public class ChangeFurnitureVariantStatusRequest : Notificator, ICommand + { + public string Id { get; set; } = null!; + public StatusEnum Status { get; set; } + + public bool Validate() + { + return !string.IsNullOrWhiteSpace(Id); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/Common/Dimensions.cs b/Core.Inventory.Application/UseCases/Inventory/Input/Common/Dimensions.cs new file mode 100644 index 0000000..26c61ff --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/Common/Dimensions.cs @@ -0,0 +1,18 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** + +namespace Core.Inventory.Application.UseCases.Inventory.Input.Common +{ + /// + /// Value object representing the dimensions of a furniture item. + /// + public class Dimensions + { + public float Width { get; set; } + public float Height { get; set; } + public float Depth { get; set; } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/CreateFurnitureBaseRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/CreateFurnitureBaseRequest.cs new file mode 100644 index 0000000..e1aad86 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/CreateFurnitureBaseRequest.cs @@ -0,0 +1,33 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Core.Inventory.Application.UseCases.Inventory.Input.Common; +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + /// + /// Command for creating a new furniture base entity. + /// + public class CreateFurnitureBaseRequest : Notificator, ICommand + { + public string ModelName { get; set; } = null!; + public string Material { get; set; } = null!; + public string Condition { get; set; } = null!; + public string? BaseDescription { get; set; } + public string? Representation { get; set; } + public string? MaintenanceNotes { get; set; } + + public Dimensions Dimensions { get; set; } = new(); + + public List? VariantIds { get; set; } + public bool Validate() + { + return !string.IsNullOrWhiteSpace(ModelName) && + !string.IsNullOrWhiteSpace(Material) && + !string.IsNullOrWhiteSpace(Condition); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/CreateFurnitureVariantRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/CreateFurnitureVariantRequest.cs new file mode 100644 index 0000000..926feda --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/CreateFurnitureVariantRequest.cs @@ -0,0 +1,37 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + public class CreateFurnitureVariantRequest : Notificator, ICommand + { + public string ModelId { get; set; } = null!; + public string Name { get; set; } = null!; + public string Color { get; set; } = null!; + public string? Line { get; set; } + + public decimal Price { get; set; } + public string Currency { get; set; } = "USD"; + public int Stock { get; set; } + + public Guid CategoryId { get; set; } + public Guid ProviderId { get; set; } + + public Dictionary Attributes { get; set; } = []; + + public bool Validate() + { + return !string.IsNullOrWhiteSpace(ModelId) + && !string.IsNullOrWhiteSpace(Name) + && !string.IsNullOrWhiteSpace(Color) + && Price >= 0 + && Stock >= 0 + && CategoryId != Guid.Empty + && ProviderId != Guid.Empty; + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/GetAllFurnitureBaseRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/GetAllFurnitureBaseRequest.cs new file mode 100644 index 0000000..ec3b772 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/GetAllFurnitureBaseRequest.cs @@ -0,0 +1,14 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + public class GetAllFurnitureBaseRequest : ICommand + { + public bool Validate() => true; + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/GetAllFurnitureVariantsByModelIdRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/GetAllFurnitureVariantsByModelIdRequest.cs new file mode 100644 index 0000000..7b5b1d2 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/GetAllFurnitureVariantsByModelIdRequest.cs @@ -0,0 +1,19 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + public class GetAllFurnitureVariantsByModelIdRequest : Notificator, ICommand + { + public string ModelId { get; set; } = null!; + + public bool Validate() + { + return !string.IsNullOrWhiteSpace(ModelId); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/GetFurnitureBaseByIdRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/GetFurnitureBaseByIdRequest.cs new file mode 100644 index 0000000..f59d0c1 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/GetFurnitureBaseByIdRequest.cs @@ -0,0 +1,22 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + /// + /// Query to retrieve a furniture base by its identifier. + /// + public class GetFurnitureBaseByIdRequest : Notificator, ICommand + { + public string Id { get; set; } = null!; + + public bool Validate() + { + return !string.IsNullOrWhiteSpace(Id); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/GetFurnitureVariantByIdRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/GetFurnitureVariantByIdRequest.cs new file mode 100644 index 0000000..a8bcdb5 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/GetFurnitureVariantByIdRequest.cs @@ -0,0 +1,19 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + public class GetFurnitureVariantByIdRequest : Notificator, ICommand + { + public string Id { get; set; } = null!; + + public bool Validate() + { + return !string.IsNullOrWhiteSpace(Id); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/UpdateFurnitureBaseRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/UpdateFurnitureBaseRequest.cs new file mode 100644 index 0000000..19964ce --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/UpdateFurnitureBaseRequest.cs @@ -0,0 +1,35 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Core.Inventory.Application.UseCases.Inventory.Input.Common; +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + /// + /// Command for updating an existing furniture base entity. + /// + public class UpdateFurnitureBaseRequest : Notificator, ICommand + { + public string Id { get; set; } = null!; + public string ModelName { get; set; } = null!; + public string Material { get; set; } = null!; + public string Condition { get; set; } = null!; + public string? BaseDescription { get; set; } + public string? Representation { get; set; } + public string? MaintenanceNotes { get; set; } + + public Dimensions Dimensions { get; set; } = new(); + + public List? VariantIds { get; set; } + public bool Validate() + { + return !string.IsNullOrWhiteSpace(Id) + && !string.IsNullOrWhiteSpace(ModelName) + && !string.IsNullOrWhiteSpace(Material) + && !string.IsNullOrWhiteSpace(Condition); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Input/UpdateFurnitureVariantRequest.cs b/Core.Inventory.Application/UseCases/Inventory/Input/UpdateFurnitureVariantRequest.cs new file mode 100644 index 0000000..da98059 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Input/UpdateFurnitureVariantRequest.cs @@ -0,0 +1,41 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** + +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Input +{ + /// + /// Command for updating an existing furniture variant. + /// + public class UpdateFurnitureVariantRequest : Notificator, ICommand + { + public string Id { get; set; } = null!; + public string ModelId { get; set; } = null!; + public string Name { get; set; } = null!; + public string Color { get; set; } = null!; + public string? Line { get; set; } + + public int Stock { get; set; } + public decimal Price { get; set; } + public string Currency { get; set; } = "USD"; + + public Guid CategoryId { get; set; } + public Guid ProviderId { get; set; } + + public Dictionary Attributes { get; set; } = []; + + public bool Validate() + { + return !string.IsNullOrWhiteSpace(Id) && + !string.IsNullOrWhiteSpace(ModelId) && + !string.IsNullOrWhiteSpace(Name) && + !string.IsNullOrWhiteSpace(Color) && + Price > 0 && + Stock >= 0; + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/InventoryHandler.cs b/Core.Inventory.Application/UseCases/Inventory/InventoryHandler.cs new file mode 100644 index 0000000..bbfc032 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/InventoryHandler.cs @@ -0,0 +1,324 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using Core.Inventory.Application.UseCases.Inventory.Ports; +using Core.Inventory.External.Clients; +using Core.Inventory.External.Clients.Requests; +using FluentValidation; +using Lib.Architecture.BuildingBlocks; +using Lib.Architecture.BuildingBlocks.Helpers; + +namespace Core.Inventory.Application.UseCases.Inventory +{ + public class InventoryHandler : + // FurnitureBase + IComponentHandler, + IComponentHandler, + IComponentHandler, + IComponentHandler, + IComponentHandler, + // FurnitureVariant + IComponentHandler, + IComponentHandler, + IComponentHandler, + IComponentHandler, + IComponentHandler + { + // FurnitureBase + private readonly IFurnitureBasePort _basePort; + private readonly IValidator _createBaseValidator; + private readonly IValidator _updateBaseValidator; + private readonly IValidator _changeBaseStatusValidator; + + // FurnitureVariant + private readonly IFurnitureVariantPort _variantPort; + private readonly IValidator _createVariantValidator; + private readonly IValidator _updateVariantValidator; + private readonly IValidator _changeVariantStatusValidator; + + private readonly IInventoryServiceClient _inventoryDALService; + + public InventoryHandler( + // FurnitureBase + IFurnitureBasePort basePort, + IValidator createBaseValidator, + IValidator updateBaseValidator, + IValidator changeBaseStatusValidator, + + // FurnitureVariant + IFurnitureVariantPort variantPort, + IValidator createVariantValidator, + IValidator updateVariantValidator, + IValidator changeVariantStatusValidator, + + IInventoryServiceClient inventoryDALService + ) + { + _basePort= basePort; + _createBaseValidator= createBaseValidator; + _updateBaseValidator= updateBaseValidator; + _changeBaseStatusValidator= changeBaseStatusValidator; + _variantPort= variantPort; + _createVariantValidator= createVariantValidator; + _updateVariantValidator= updateVariantValidator; + _changeVariantStatusValidator= changeVariantStatusValidator; + _inventoryDALService= inventoryDALService; + } + + #region FurnitureBase + public async ValueTask ExecuteAsync(CreateFurnitureBaseRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + if (!command.IsValid(_createBaseValidator)) + { + _basePort.ValidationErrors(command.Notifications); + return; + } + + var request = new FurnitureBaseRequest + { + BaseDescription = command.BaseDescription, + Condition = command.Condition, + MaintenanceNotes = command.MaintenanceNotes, + Material = command.Material, + ModelName = command.ModelName, + Representation = command.Representation, + VariantIds = command.VariantIds, + Dimensions = new Adapters.Lib.Dimensions + { + Depth = command.Dimensions.Depth, + Height = command.Dimensions.Height, + Width = command.Dimensions.Width + } + }; + + var result = await _inventoryDALService.CreateFurnitureBaseAsync(request, cancellationToken); + _basePort.Success(result); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _basePort); + } + } + + public async ValueTask ExecuteAsync(UpdateFurnitureBaseRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + if (!command.IsValid(_updateBaseValidator)) + { + _basePort.ValidationErrors(command.Notifications); + return; + } + + var request = new FurnitureBaseRequest + { + BaseDescription = command.BaseDescription, + Condition = command.Condition, + MaintenanceNotes = command.MaintenanceNotes, + Material = command.Material, + ModelName = command.ModelName, + Representation = command.Representation, + VariantIds = command.VariantIds, + Dimensions = new Adapters.Lib.Dimensions + { + Depth = command.Dimensions.Depth, + Height = command.Dimensions.Height, + Width = command.Dimensions.Width + } + }; + + var result = await _inventoryDALService.UpdateFurnitureBaseAsync(request, command.Id, cancellationToken); + _basePort.Success(result); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _basePort); + } + } + + public async ValueTask ExecuteAsync(GetAllFurnitureBaseRequest command, CancellationToken cancellationToken = default) + { + try + { + var result = await _inventoryDALService.GetAllFurnitureBaseAsync(cancellationToken); + if (!result.Any()) + { + _basePort.NoContentSuccess(); + return; + } + _basePort.Success([.. result]); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _basePort); + } + } + + public async ValueTask ExecuteAsync(GetFurnitureBaseByIdRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + var result = await _inventoryDALService.GetFurnitureBaseByIdAsync(command.Id, cancellationToken); + if (result is null) + { + _basePort.NoContentSuccess(); + return; + } + _basePort.Success(result); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _basePort); + } + } + + public async ValueTask ExecuteAsync(ChangeFurnitureBaseStatusRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + if (!command.IsValid(_changeBaseStatusValidator)) + { + _basePort.ValidationErrors(command.Notifications); + return; + } + + var result = await _inventoryDALService.ChangeFurnitureBaseStatusAsync(command.Id, command.Status, cancellationToken); + _basePort.Success(result); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _basePort); + } + } + + #endregion + + #region FurnitureVariant + public async ValueTask ExecuteAsync(CreateFurnitureVariantRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + if (!command.IsValid(_createVariantValidator)) + { + _variantPort.ValidationErrors(command.Notifications); + return; + } + var request = new FurnitureVariantRequest + { + Stock = command.Stock, + Attributes= command.Attributes, + CategoryId = command.CategoryId, + Color = command.Color, + Currency = command.Currency, + Line = command.Line, + ModelId = command.ModelId, + Name = command.Name, + Price = command.Price, + ProviderId = command.ProviderId + }; + var result = await _inventoryDALService.CreateFurnitureVariantAsync(request, cancellationToken); + _variantPort.Success(result); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _variantPort); + } + } + + public async ValueTask ExecuteAsync(UpdateFurnitureVariantRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + if (!command.IsValid(_updateVariantValidator)) + { + _variantPort.ValidationErrors(command.Notifications); + return; + } + var request = new FurnitureVariantRequest + { + Stock = command.Stock, + Attributes= command.Attributes, + CategoryId = command.CategoryId, + Color = command.Color, + Currency = command.Currency, + Line = command.Line, + ModelId = command.ModelId, + Name = command.Name, + Price = command.Price, + ProviderId = command.ProviderId + }; + var result = await _inventoryDALService.UpdateFurnitureVariantAsync(request, command.Id, cancellationToken); + _variantPort.Success(result); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _variantPort); + } + } + + public async ValueTask ExecuteAsync(GetFurnitureVariantByIdRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + var result = await _inventoryDALService.GetFurnitureVariantByIdAsync(command.Id, cancellationToken); + if (result is null) + { + _variantPort.NoContentSuccess(); + return; + } + _variantPort.Success(result); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _variantPort); + } + } + + public async ValueTask ExecuteAsync(GetAllFurnitureVariantsByModelIdRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + var result = await _inventoryDALService.GetAllVariantsByModelIdAsync(command.ModelId, cancellationToken); + if (!result.Any()) + { + _variantPort.NoContentSuccess(); + return; + } + _variantPort.Success([.. result]); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _variantPort); + } + } + + public async ValueTask ExecuteAsync(ChangeFurnitureVariantStatusRequest command, CancellationToken cancellationToken = default) + { + try + { + ArgumentNullException.ThrowIfNull(command); + if (!command.IsValid(_changeVariantStatusValidator)) + { + _variantPort.ValidationErrors(command.Notifications); + return; + } + var result = await _inventoryDALService.ChangeFurnitureVariantStatusAsync(command.Id, command.Status, cancellationToken); + _variantPort.Success(result); + } + catch (Exception ex) + { + ApiResponseHelper.EvaluatePort(ex, _variantPort); + } + } + + #endregion + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Ports/IFurnitureBasePort.cs b/Core.Inventory.Application/UseCases/Inventory/Ports/IFurnitureBasePort.cs new file mode 100644 index 0000000..d35d30b --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Ports/IFurnitureBasePort.cs @@ -0,0 +1,22 @@ +// *********************************************************************** +// +// Core.Inventory.Application +// +// *********************************************************************** +using Core.Adapters.Lib; +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Ports +{ + /// + /// Output port interface for handling results from FurnitureBase use cases. + /// + public interface IFurnitureBasePort : IBasePort, + ICommandSuccessPort, + ICommandSuccessPort>, + INoContentPort, IBusinessErrorPort, ITimeoutPort, IValidationErrorPort, + INotFoundPort, IForbiddenPort, IUnauthorizedPort, IInternalServerErrorPort, + IBadRequestPort + { + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Ports/IFurnitureVariantPort.cs b/Core.Inventory.Application/UseCases/Inventory/Ports/IFurnitureVariantPort.cs new file mode 100644 index 0000000..b47703f --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Ports/IFurnitureVariantPort.cs @@ -0,0 +1,22 @@ +// *********************************************************************** +// +// Core.Inventory.Application +// +// *********************************************************************** +using Core.Adapters.Lib; +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Application.UseCases.Inventory.Ports +{ + /// + /// Output port interface for handling results from FurnitureVariant use cases. + /// + public interface IFurnitureVariantPort : IBasePort, + ICommandSuccessPort, + ICommandSuccessPort>, + INoContentPort, IBusinessErrorPort, ITimeoutPort, IValidationErrorPort, + INotFoundPort, IForbiddenPort, IUnauthorizedPort, IInternalServerErrorPort, + IBadRequestPort + { + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/ChangeFurnitureBaseStatusValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/ChangeFurnitureBaseStatusValidator.cs new file mode 100644 index 0000000..7220c93 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/ChangeFurnitureBaseStatusValidator.cs @@ -0,0 +1,17 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class ChangeFurnitureBaseStatusValidator : AbstractValidator + { + public ChangeFurnitureBaseStatusValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required."); + + RuleFor(x => x.Status) + .IsInEnum().WithMessage("Invalid status value."); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/ChangeFurnitureVariantStatusValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/ChangeFurnitureVariantStatusValidator.cs new file mode 100644 index 0000000..31ccbd9 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/ChangeFurnitureVariantStatusValidator.cs @@ -0,0 +1,17 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class ChangeFurnitureVariantStatusValidator : AbstractValidator + { + public ChangeFurnitureVariantStatusValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required."); + + RuleFor(x => x.Status) + .IsInEnum().WithMessage("Invalid status value."); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/Common/DimensionsValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/Common/DimensionsValidator.cs new file mode 100644 index 0000000..81bdf40 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/Common/DimensionsValidator.cs @@ -0,0 +1,20 @@ +using Core.Inventory.Application.UseCases.Inventory.Input.Common; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator.Common +{ + public class DimensionsValidator : AbstractValidator + { + public DimensionsValidator() + { + RuleFor(x => x.Width) + .GreaterThan(0).WithMessage("Width must be greater than 0."); + + RuleFor(x => x.Height) + .GreaterThan(0).WithMessage("Height must be greater than 0."); + + RuleFor(x => x.Depth) + .GreaterThan(0).WithMessage("Depth must be greater than 0."); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/CreateFurnitureBaseValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/CreateFurnitureBaseValidator.cs new file mode 100644 index 0000000..ca24a1b --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/CreateFurnitureBaseValidator.cs @@ -0,0 +1,29 @@ +// *********************************************************************** +// +// Core.Inventory +// +// *********************************************************************** +using Core.Inventory.Application.UseCases.Inventory.Input; +using Core.Inventory.Application.UseCases.Inventory.Validator.Common; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class CreateFurnitureBaseValidator : AbstractValidator + { + public CreateFurnitureBaseValidator() + { + RuleFor(x => x.ModelName) + .NotEmpty().WithMessage("Model name is required."); + + RuleFor(x => x.Material) + .NotEmpty().WithMessage("Material is required."); + + RuleFor(x => x.Condition) + .NotEmpty().WithMessage("Condition is required."); + + RuleFor(x => x.Dimensions) + .SetValidator(new DimensionsValidator()); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/CreateFurnitureVariantValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/CreateFurnitureVariantValidator.cs new file mode 100644 index 0000000..62bf54b --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/CreateFurnitureVariantValidator.cs @@ -0,0 +1,35 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class CreateFurnitureVariantValidator : AbstractValidator + { + public CreateFurnitureVariantValidator() + { + RuleFor(x => x.ModelId) + .NotEmpty().WithMessage("ModelId is required."); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required."); + + RuleFor(x => x.Color) + .NotEmpty().WithMessage("Color is required."); + + RuleFor(x => x.Price) + .GreaterThanOrEqualTo(0).WithMessage("Price must be a non-negative value."); + + RuleFor(x => x.Stock) + .GreaterThanOrEqualTo(0).WithMessage("Stock must be a non-negative value."); + + RuleFor(x => x.CategoryId) + .NotEqual(Guid.Empty).WithMessage("CategoryId is required."); + + RuleFor(x => x.ProviderId) + .NotEqual(Guid.Empty).WithMessage("ProviderId is required."); + + RuleFor(x => x.Currency) + .NotEmpty().WithMessage("Currency is required."); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/GetAllFurnitureVariantsByModelIdValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/GetAllFurnitureVariantsByModelIdValidator.cs new file mode 100644 index 0000000..de3f3f9 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/GetAllFurnitureVariantsByModelIdValidator.cs @@ -0,0 +1,14 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class GetAllFurnitureVariantsByModelIdValidator : AbstractValidator + { + public GetAllFurnitureVariantsByModelIdValidator() + { + RuleFor(x => x.ModelId) + .NotEmpty().WithMessage("ModelId is required."); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/GetFurnitureBaseByIdValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/GetFurnitureBaseByIdValidator.cs new file mode 100644 index 0000000..af79464 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/GetFurnitureBaseByIdValidator.cs @@ -0,0 +1,14 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class GetFurnitureBaseByIdValidator : AbstractValidator + { + public GetFurnitureBaseByIdValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required."); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/GetFurnitureVariantByIdValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/GetFurnitureVariantByIdValidator.cs new file mode 100644 index 0000000..66e5c63 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/GetFurnitureVariantByIdValidator.cs @@ -0,0 +1,14 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class GetFurnitureVariantByIdValidator : AbstractValidator + { + public GetFurnitureVariantByIdValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required."); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/UpdateFurnitureBaseValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/UpdateFurnitureBaseValidator.cs new file mode 100644 index 0000000..a8860ce --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/UpdateFurnitureBaseValidator.cs @@ -0,0 +1,24 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using Core.Inventory.Application.UseCases.Inventory.Validator.Common; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class UpdateFurnitureBaseValidator : AbstractValidator + { + public UpdateFurnitureBaseValidator() + { + RuleFor(x => x.ModelName) + .NotEmpty().WithMessage("Model name is required."); + + RuleFor(x => x.Material) + .NotEmpty().WithMessage("Material is required."); + + RuleFor(x => x.Condition) + .NotEmpty().WithMessage("Condition is required."); + + RuleFor(x => x.Dimensions) + .SetValidator(new DimensionsValidator()); + } + } +} diff --git a/Core.Inventory.Application/UseCases/Inventory/Validator/UpdateFurnitureVariantValidator.cs b/Core.Inventory.Application/UseCases/Inventory/Validator/UpdateFurnitureVariantValidator.cs new file mode 100644 index 0000000..6fc1183 --- /dev/null +++ b/Core.Inventory.Application/UseCases/Inventory/Validator/UpdateFurnitureVariantValidator.cs @@ -0,0 +1,38 @@ +using Core.Inventory.Application.UseCases.Inventory.Input; +using FluentValidation; + +namespace Core.Inventory.Application.UseCases.Inventory.Validator +{ + public class UpdateFurnitureVariantValidator : AbstractValidator + { + public UpdateFurnitureVariantValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required."); + + RuleFor(x => x.ModelId) + .NotEmpty().WithMessage("ModelId is required."); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required."); + + RuleFor(x => x.Color) + .NotEmpty().WithMessage("Color is required."); + + RuleFor(x => x.Price) + .GreaterThan(0).WithMessage("Price must be greater than 0."); + + RuleFor(x => x.Stock) + .GreaterThanOrEqualTo(0).WithMessage("Stock must be a non-negative value."); + + RuleFor(x => x.Currency) + .NotEmpty().WithMessage("Currency is required."); + + RuleFor(x => x.CategoryId) + .NotEqual(Guid.Empty).WithMessage("CategoryId is required."); + + RuleFor(x => x.ProviderId) + .NotEqual(Guid.Empty).WithMessage("ProviderId is required."); + } + } +} diff --git a/Core.Inventory.External/ClientConfiguration/RegisterClientConfiguration.cs b/Core.Inventory.External/ClientConfiguration/RegisterClientConfiguration.cs new file mode 100644 index 0000000..56dac2a --- /dev/null +++ b/Core.Inventory.External/ClientConfiguration/RegisterClientConfiguration.cs @@ -0,0 +1,58 @@ +using Core.Inventory.External.Clients; +using Core.Inventory.External.GatewayConfigurations; +using Core.Inventory.External.Helpers; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Refit; + +namespace Core.Inventory.External.ClientConfiguration +{ + public static class RegisterClientConfiguration + { + public static IServiceCollection RegisterExternalLayer(this IServiceCollection services, IConfiguration configuration) + { + var gatewayConfiguration = new GatewayConfiguration(); + var gatewaySettingsConfiguration = new GatewaySettingsConfigurations(configuration); + + // Register GatewayConfiguration as a singleton + services.AddSingleton(gatewayConfiguration); + + // Register IHttpContextAccessor + services.AddSingleton(); + + // Register ITokenProvider + services.AddSingleton(); + + // Register the custom AuthenticatedHttpClientHandler + services.AddTransient(provider => + { + var tokenProvider = provider.GetRequiredService(); + var handler = new AuthenticatedHttpClientHandler(tokenProvider) + { + InnerHandler = new HttpClientHandler() + }; + return handler; + }); + + var inventoryServiceApiUrl = GatewaySettingsConfigurations.GetInventoryServiceAPIEndpoint().Endpoint.Url; + + // Register IInventoryServiceClient with the manually created HttpClient + services.AddScoped(provider => + { + var handler = provider.GetRequiredService(); + var httpClient = new HttpClient(handler) + { + BaseAddress = new Uri(inventoryServiceApiUrl), + Timeout = TimeSpan.FromMinutes(1) + }; + return RestService.For(httpClient); + }); + + services.AddScoped(); + + return services; + } + } +} diff --git a/Core.Inventory.External/Clients/IInventoryServiceClient.cs b/Core.Inventory.External/Clients/IInventoryServiceClient.cs new file mode 100644 index 0000000..f8eef21 --- /dev/null +++ b/Core.Inventory.External/Clients/IInventoryServiceClient.cs @@ -0,0 +1,49 @@ +using Core.Adapters.Lib; +using Core.Blueprint.Mongo; +using Core.Inventory.External.Clients.Requests; +using Microsoft.AspNetCore.Mvc; +using Refit; + +namespace Core.Inventory.External.Clients +{ + public interface IInventoryServiceClient + { + #region FurnitureBase + + [Get("/api/v1/FurnitureBase")] + Task> GetAllFurnitureBaseAsync(CancellationToken cancellationToken = default); + + [Get("/api/v1/FurnitureBase/{id}")] + Task GetFurnitureBaseByIdAsync([FromRoute] string id, CancellationToken cancellationToken = default); + + [Post("/api/v1/FurnitureBase")] + Task CreateFurnitureBaseAsync([FromBody] FurnitureBaseRequest request, CancellationToken cancellationToken = default); + + [Put("/api/v1/FurnitureBase/{id}")] + Task UpdateFurnitureBaseAsync([FromBody] FurnitureBaseRequest request, [FromRoute] string id, CancellationToken cancellationToken = default); + + [Patch("/api/v1/FurnitureBase/{id}/{newStatus}/ChangeStatus")] + Task ChangeFurnitureBaseStatusAsync([FromRoute] string id, [FromRoute] StatusEnum newStatus, CancellationToken cancellationToken = default); + + #endregion + + #region FurnitureVariant + + [Get("/api/v1/FurnitureVariant/{modelId}")] + Task> GetAllVariantsByModelIdAsync([FromRoute] string modelId, CancellationToken cancellationToken = default); + + [Get("/api/v1/FurnitureVariant/{id}/byId")] + Task GetFurnitureVariantByIdAsync([FromRoute] string id, CancellationToken cancellationToken = default); + + [Post("/api/v1/FurnitureVariant")] + Task CreateFurnitureVariantAsync([FromBody] FurnitureVariantRequest request, CancellationToken cancellationToken = default); + + [Put("/api/v1/FurnitureVariant/{id}")] + Task UpdateFurnitureVariantAsync([FromBody] FurnitureVariantRequest request, [FromRoute] string id, CancellationToken cancellationToken = default); + + [Patch("/api/v1/FurnitureVariant/{id}/{newStatus}/ChangeStatus")] + Task ChangeFurnitureVariantStatusAsync([FromRoute] string id, [FromRoute] StatusEnum newStatus, CancellationToken cancellationToken = default); + + #endregion + } +} diff --git a/Core.Inventory.External/Clients/Requests/FurnitureBaseRequest.cs b/Core.Inventory.External/Clients/Requests/FurnitureBaseRequest.cs new file mode 100644 index 0000000..cf662ab --- /dev/null +++ b/Core.Inventory.External/Clients/Requests/FurnitureBaseRequest.cs @@ -0,0 +1,17 @@ +using Core.Adapters.Lib; + +namespace Core.Inventory.External.Clients.Requests +{ + public class FurnitureBaseRequest + { + public string ModelName { get; set; } = null!; + public string Material { get; set; } = null!; + public string Condition { get; set; } = null!; + public string? BaseDescription { get; set; } + public string? Representation { get; set; } + public string? MaintenanceNotes { get; set; } + + public Dimensions Dimensions { get; set; } = new(); + public List? VariantIds { get; set; } + } +} diff --git a/Core.Inventory.External/Clients/Requests/FurnitureVariantRequest.cs b/Core.Inventory.External/Clients/Requests/FurnitureVariantRequest.cs new file mode 100644 index 0000000..eecb1aa --- /dev/null +++ b/Core.Inventory.External/Clients/Requests/FurnitureVariantRequest.cs @@ -0,0 +1,19 @@ +namespace Core.Inventory.External.Clients.Requests +{ + public class FurnitureVariantRequest + { + public string ModelId { get; set; } = null!; + public string Name { get; set; } = null!; + public string Color { get; set; } = null!; + public string? Line { get; set; } + + public decimal Price { get; set; } + public string Currency { get; set; } = "USD"; + public int Stock { get; set; } + + public Guid CategoryId { get; set; } + public Guid ProviderId { get; set; } + + public Dictionary Attributes { get; set; } = []; + } +} diff --git a/Core.Inventory.External/Core.Inventory.External.csproj b/Core.Inventory.External/Core.Inventory.External.csproj new file mode 100644 index 0000000..f8d46c4 --- /dev/null +++ b/Core.Inventory.External/Core.Inventory.External.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + + + + + + + + + diff --git a/Core.Inventory.External/GatewayConfigurations/GatewayConfiguration.cs b/Core.Inventory.External/GatewayConfigurations/GatewayConfiguration.cs new file mode 100644 index 0000000..736f119 --- /dev/null +++ b/Core.Inventory.External/GatewayConfigurations/GatewayConfiguration.cs @@ -0,0 +1,20 @@ +using Core.Blueprint.External; + +namespace Core.Inventory.External.GatewayConfigurations +{ + public record GatewayConfiguration + { + public GatewayConfiguration() + { + InventoryService = new InventoryServiceAPI(); + } + + public InventoryServiceAPI InventoryService { get; set; } + } + + public record InventoryServiceAPI + { + public string Channel { get; set; } + public BaseEndpoint Endpoint { get; set; } + } +} diff --git a/Core.Inventory.External/GatewayConfigurations/GatewaySettingsConfigurations.cs b/Core.Inventory.External/GatewayConfigurations/GatewaySettingsConfigurations.cs new file mode 100644 index 0000000..d7a2d91 --- /dev/null +++ b/Core.Inventory.External/GatewayConfigurations/GatewaySettingsConfigurations.cs @@ -0,0 +1,52 @@ +using Core.Blueprint.External; +using Microsoft.Extensions.Configuration; + +namespace Core.Inventory.External.GatewayConfigurations +{ + public class GatewaySettingsConfigurations + { + private static GatewayConfiguration GatewayConfigurations { get; set; } = new GatewayConfiguration(); + private readonly IConfiguration _configuration; + + public GatewaySettingsConfigurations(IConfiguration configuration) + { + _configuration = configuration; + this.SetInventoryServiceAPIEndpoint(); + } + + public static InventoryServiceAPI GetInventoryServiceAPIEndpoint() + { + return GatewayConfigurations.InventoryService; + } + + private GatewayConfiguration SetInventoryServiceAPIEndpoint() + { + IConfigurationSection source; + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty; + + if (environment == "Local") + source = _configuration.GetSection("LocalGateways"); + else + source = _configuration.GetSection("Gateways"); + + var endpoint = source["InventoryDAL"] ?? string.Empty; + + if (string.IsNullOrEmpty(endpoint)) + throw new Exception("Inventory DAL endpoint is empty or null"); + + GatewayConfigurations.InventoryService = new InventoryServiceAPI() + { + Endpoint = new BaseEndpoint() + { + Uri = new Uri(endpoint), + Url = endpoint, + Token = string.Empty, + APIName = "Inventory Service" + } + }; + + return GatewayConfigurations; + } + + } +} diff --git a/Core.Inventory.External/Helpers/AuthenticatedHttpClientHandler.cs b/Core.Inventory.External/Helpers/AuthenticatedHttpClientHandler.cs new file mode 100644 index 0000000..8d99bb8 --- /dev/null +++ b/Core.Inventory.External/Helpers/AuthenticatedHttpClientHandler.cs @@ -0,0 +1,26 @@ +namespace Core.Inventory.External.Helpers +{ + /// + /// Class to inject the token in all requests. + /// + public class AuthenticatedHttpClientHandler : DelegatingHandler + { + private readonly ITokenProvider _tokenProvider; + + public AuthenticatedHttpClientHandler(ITokenProvider tokenProvider) + { + _tokenProvider = tokenProvider; + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var token = _tokenProvider.GetToken(); + if (!string.IsNullOrEmpty(token)) + { + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + } + + return await base.SendAsync(request, cancellationToken); + } + } +} diff --git a/Core.Inventory.External/Helpers/HttpContextTokenProvider.cs b/Core.Inventory.External/Helpers/HttpContextTokenProvider.cs new file mode 100644 index 0000000..5d32356 --- /dev/null +++ b/Core.Inventory.External/Helpers/HttpContextTokenProvider.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Http; + +namespace Core.Inventory.External.Helpers +{ + /// + /// Class to return the access token to controllers. + /// + public class HttpContextTokenProvider : ITokenProvider + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public HttpContextTokenProvider(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + /// + /// Get token from headers. + /// + public string GetToken() + { + return _httpContextAccessor.HttpContext?.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); + } + } +} diff --git a/Core.Inventory.External/Helpers/ITokenProvider.cs b/Core.Inventory.External/Helpers/ITokenProvider.cs new file mode 100644 index 0000000..90800d1 --- /dev/null +++ b/Core.Inventory.External/Helpers/ITokenProvider.cs @@ -0,0 +1,13 @@ +namespace Core.Inventory.External.Helpers +{ + /// + /// Interface for token provider. + /// + public interface ITokenProvider + { + /// + /// Get token from headers. + /// + string GetToken(); + } +} diff --git a/Core.Inventory.Service.API/Controllers/FurnitureBaseController.cs b/Core.Inventory.Service.API/Controllers/FurnitureBaseController.cs new file mode 100644 index 0000000..e3de54c --- /dev/null +++ b/Core.Inventory.Service.API/Controllers/FurnitureBaseController.cs @@ -0,0 +1,77 @@ +using Asp.Versioning; +using Core.Inventory.Application.UseCases.Inventory.Input; +using Core.Inventory.Application.UseCases.Inventory.Ports; +using Lib.Architecture.BuildingBlocks; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Inventory.Service.API.Controllers +{ + [ApiVersion("1.0")] + [Route("api/v{api-version:apiVersion}/[controller]")] + [Produces("application/json")] + [ApiController] + public class FurnitureBaseController : ControllerBase + { + private readonly IComponentHandler _getByIdHandler; + private readonly IComponentHandler _getAllHandler; + private readonly IComponentHandler _createHandler; + private readonly IComponentHandler _updateHandler; + private readonly IComponentHandler _changeStatusHandler; + private readonly IFurnitureBasePort _port; + + public FurnitureBaseController( + IComponentHandler getByIdHandler, + IComponentHandler getAllHandler, + IComponentHandler createHandler, + IComponentHandler updateHandler, + IComponentHandler changeStatusHandler, + IFurnitureBasePort port) + { + _getByIdHandler= getByIdHandler; + _getAllHandler= getAllHandler; + _createHandler= createHandler; + _updateHandler= updateHandler; + _changeStatusHandler= changeStatusHandler; + _port= port; + } + + [HttpGet("GetAll")] + public async Task GetAllAsync(CancellationToken cancellationToken) + { + await _getAllHandler.ExecuteAsync(new GetAllFurnitureBaseRequest { }, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + + [HttpPost("GetById")] + public async Task GetByIdAsync([FromBody] GetFurnitureBaseByIdRequest request, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(request?.Id)) return BadRequest("Furniture base identifier is required"); + + await _getByIdHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + + [HttpPost("Create")] + public async Task CreateAsync([FromBody] CreateFurnitureBaseRequest request, CancellationToken cancellationToken) + { + await _createHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + + [HttpPut("Update")] + public async Task UpdateAsync([FromBody] UpdateFurnitureBaseRequest request, CancellationToken cancellationToken) + { + await _updateHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + + [HttpPatch("ChangeStatus")] + public async Task ChangeStatusAsync([FromBody] ChangeFurnitureBaseStatusRequest request, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(request?.Id)) return BadRequest("Furniture base identifier is required"); + + await _changeStatusHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + } +} diff --git a/Core.Inventory.Service.API/Controllers/FurnitureVariantController.cs b/Core.Inventory.Service.API/Controllers/FurnitureVariantController.cs new file mode 100644 index 0000000..7e6bb78 --- /dev/null +++ b/Core.Inventory.Service.API/Controllers/FurnitureVariantController.cs @@ -0,0 +1,70 @@ +using Asp.Versioning; +using Core.Inventory.Application.UseCases.Inventory.Input; +using Core.Inventory.Application.UseCases.Inventory.Ports; +using Lib.Architecture.BuildingBlocks; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Inventory.Service.API.Controllers +{ + [ApiVersion("1.0")] + [Route("api/v{api-version:apiVersion}/[controller]")] + [Produces("application/json")] + [ApiController] + public class FurnitureVariantController( + IComponentHandler getByIdHandler, + IComponentHandler getAllHandler, + IComponentHandler createHandler, + IComponentHandler updateHandler, + IComponentHandler changeStatusHandler, + IFurnitureVariantPort port) : ControllerBase + { + private readonly IComponentHandler _getByIdHandler = getByIdHandler; + private readonly IComponentHandler _getAllHandler = getAllHandler; + private readonly IComponentHandler _createHandler = createHandler; + private readonly IComponentHandler _updateHandler = updateHandler; + private readonly IComponentHandler _changeStatusHandler = changeStatusHandler; + private readonly IFurnitureVariantPort _port = port; + + [HttpGet("GetAllByModelId")] + public async Task GetAllAsync([FromQuery] string modelId, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(modelId)) return BadRequest("Model ID is required"); + + var request = new GetAllFurnitureVariantsByModelIdRequest { ModelId = modelId }; + await _getAllHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + + [HttpPost("GetById")] + public async Task GetByIdAsync([FromBody] GetFurnitureVariantByIdRequest request, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(request?.Id)) return BadRequest("Furniture variant identifier is required"); + + await _getByIdHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + + [HttpPost("Create")] + public async Task CreateAsync([FromBody] CreateFurnitureVariantRequest request, CancellationToken cancellationToken) + { + await _createHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + + [HttpPut("Update")] + public async Task UpdateAsync([FromBody] UpdateFurnitureVariantRequest request, CancellationToken cancellationToken) + { + await _updateHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + + [HttpPatch("ChangeStatus")] + public async Task ChangeStatusAsync([FromBody] ChangeFurnitureVariantStatusRequest request, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(request?.Id)) return BadRequest("Furniture variant identifier is required"); + + await _changeStatusHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + return _port.ViewModel; + } + } +} diff --git a/Core.Inventory.Service.API/Core.Inventory.Service.API.csproj b/Core.Inventory.Service.API/Core.Inventory.Service.API.csproj new file mode 100644 index 0000000..da915c8 --- /dev/null +++ b/Core.Inventory.Service.API/Core.Inventory.Service.API.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + diff --git a/Core.Inventory.Service.API/Core.Inventory.Service.API.http b/Core.Inventory.Service.API/Core.Inventory.Service.API.http new file mode 100644 index 0000000..db426d5 --- /dev/null +++ b/Core.Inventory.Service.API/Core.Inventory.Service.API.http @@ -0,0 +1,6 @@ +@Core.Inventory.Service.API_HostAddress = http://localhost:5257 + +GET {{Core.Inventory.Service.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Core.Inventory.Service.API/Extensions/ServiceCollectionExtension.cs b/Core.Inventory.Service.API/Extensions/ServiceCollectionExtension.cs new file mode 100644 index 0000000..6d88d54 --- /dev/null +++ b/Core.Inventory.Service.API/Extensions/ServiceCollectionExtension.cs @@ -0,0 +1,65 @@ +using Core.Inventory.Application.UseCases.Inventory; +using Core.Inventory.Application.UseCases.Inventory.Adapter; +using Core.Inventory.Application.UseCases.Inventory.Input; +using Core.Inventory.Application.UseCases.Inventory.Ports; +using Core.Inventory.Application.UseCases.Inventory.Validator; +using FluentValidation; +using Lib.Architecture.BuildingBlocks; + +namespace Core.Inventory.Service.API.Extensions +{ + public static class ServiceCollectionExtension + { + public static IServiceCollection AddServiceConfigurationLayer(this IServiceCollection services) + { + #region FurnitureBase + services.AddScoped(); + + services.AddScoped, InventoryHandler>(); + services.AddScoped, InventoryHandler>(); + services.AddScoped, InventoryHandler>(); + services.AddScoped, InventoryHandler>(); + services.AddScoped, InventoryHandler>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, CreateFurnitureBaseValidator>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, UpdateFurnitureBaseValidator>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, ChangeFurnitureBaseStatusValidator>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, GetFurnitureBaseByIdValidator>(); + #endregion + + #region FurnitureVariant + services.AddScoped(); + + services.AddScoped, InventoryHandler>(); + services.AddScoped, InventoryHandler>(); + services.AddScoped, InventoryHandler>(); + services.AddScoped, InventoryHandler>(); + services.AddScoped, InventoryHandler>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, CreateFurnitureVariantValidator>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, UpdateFurnitureVariantValidator>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, ChangeFurnitureVariantStatusValidator>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, GetFurnitureVariantByIdValidator>(); + + services.AddValidatorsFromAssemblyContaining(); + services.AddScoped, GetAllFurnitureVariantsByModelIdValidator>(); + #endregion + + return services; + } + } +} diff --git a/Core.Inventory.Service.API/Extensions/SwaggerExtensions.cs b/Core.Inventory.Service.API/Extensions/SwaggerExtensions.cs new file mode 100644 index 0000000..a384f6b --- /dev/null +++ b/Core.Inventory.Service.API/Extensions/SwaggerExtensions.cs @@ -0,0 +1,98 @@ +using Asp.Versioning.ApiExplorer; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Swashbuckle.AspNetCore.SwaggerUI; + +namespace Core.Inventory.Service.API.Extensions +{ + public static class SwaggerExtensions + { + public static void AddSwagger(this IServiceCollection services, IConfiguration configuration) + { + services.AddEndpointsApiExplorer(); + AddSwaggerGen(services, configuration); + services.AddTransient, ConfigureSwaggerOptions>(); + } + + /// + /// Configures Swagger generation with OAuth2 security and XML comments. + /// + /// The to add the services to. + /// The containing Swagger and OAuth2 configuration settings. + public static void AddSwaggerGen(this IServiceCollection services, IConfiguration configuration) + { + services.AddSwaggerGen(c => + { + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + Scheme = "bearer", + BearerFormat = "JWT" + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + Array.Empty() + } + }); + }); + } + public static void ConfigureSwagger(this WebApplication app) + { + //Swagger Stuff Goes Here + + app.UseSwagger(); + app.UseSwaggerUI(options => + { + foreach (var version in app.DescribeApiVersions().Select(version => version.GroupName)) + options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version); + + options.DisplayRequestDuration(); + options.EnableTryItOutByDefault(); + options.DocExpansion(DocExpansion.None); + }); + } + + public static IServiceCollection AddVersioning(this IServiceCollection services) + { + services.AddApiVersioning(options => options.ReportApiVersions = true) + .AddApiExplorer(options => + { + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + return services; + } + } + public class ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) : IConfigureOptions + { + private readonly IApiVersionDescriptionProvider _provider = provider; + + public void Configure(SwaggerGenOptions options) + { + foreach (var description in _provider.ApiVersionDescriptions) + options.SwaggerDoc(description.GroupName, new() + { + Title = AppDomain.CurrentDomain.FriendlyName, + Version = description.ApiVersion.ToString() + }); + + + options.CustomSchemaIds(type => type.ToString().Replace("+", ".")); + } + } +} diff --git a/Core.Inventory.Service.API/Program.cs b/Core.Inventory.Service.API/Program.cs new file mode 100644 index 0000000..9cd0c92 --- /dev/null +++ b/Core.Inventory.Service.API/Program.cs @@ -0,0 +1,80 @@ +using Core.Blueprint.Logging.Configuration; +using Core.Inventory.External.ClientConfiguration; +using Core.Inventory.Service.API.Extensions; +using Microsoft.AspNetCore.HttpLogging; +using System.Reflection; +using System.Text.Json.Serialization; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddLogs(builder); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Configuration + .AddUserSecrets(Assembly.GetExecutingAssembly()) + .AddEnvironmentVariables(); + +builder.Services.RegisterExternalLayer(builder.Configuration); +builder.Services.AddServiceConfigurationLayer(); +builder.Services.AddResponseCompression(); +builder.Services.AddProblemDetails(); +builder.Services.AddMemoryCache(); + +builder.Host.ConfigureServices((context, services) => +{ + + services.AddLogging(); + services.AddControllers(); + services.AddProblemDetails(); + services.AddCors(options + => options.AddDefaultPolicy(policyBuilder + => policyBuilder + .AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod())); + + builder.Services.Configure(options => + { + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); + }); + + services + .AddEndpointsApiExplorer() + .AddVersioning() + .AddSwagger(builder.Configuration); + + services.AddHealthChecks(); + services.AddHttpLogging(options => options.LoggingFields = HttpLoggingFields.All); + + builder.Services.AddOutputCache(options => + { + options.AddBasePolicy(builder => + builder.Expire(TimeSpan.FromSeconds(10))); + options.AddPolicy("Expire20", builder => + builder.Expire(TimeSpan.FromSeconds(20))); + options.AddPolicy("Expire30", builder => + builder.Expire(TimeSpan.FromSeconds(30))); + }); + +}); + +var app = builder.Build(); + +app.UseRouting(); +app.UseSwagger(); +app.UseSwaggerUI(); +app.UseAuthentication(); +app.UseAuthorization(); +app.MapControllers(); +app.UseCors(); +app.ConfigureSwagger(); +app.UseHttpsRedirection(); +app.UseStaticFiles(); +app.UseResponseCompression(); +app.UseOutputCache(); +app.UseResponseCaching(); +app.UseLogging(builder.Configuration); +app.MapHealthChecks("/health"); + +app.Run(); diff --git a/Core.Inventory.Service.API/Properties/launchSettings.json b/Core.Inventory.Service.API/Properties/launchSettings.json new file mode 100644 index 0000000..8aa1741 --- /dev/null +++ b/Core.Inventory.Service.API/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:58212", + "sslPort": 44339 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5257", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7028;http://localhost:5257", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + } + } + } +} diff --git a/Core.Inventory.Service.API/WeatherForecast.cs b/Core.Inventory.Service.API/WeatherForecast.cs new file mode 100644 index 0000000..1daa5df --- /dev/null +++ b/Core.Inventory.Service.API/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace Core.Inventory.Service.API +{ + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} diff --git a/Core.Inventory.Service.API/appsettings.Development.json b/Core.Inventory.Service.API/appsettings.Development.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/Core.Inventory.Service.API/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Core.Inventory.Service.API/appsettings.Local.json b/Core.Inventory.Service.API/appsettings.Local.json new file mode 100644 index 0000000..740f657 --- /dev/null +++ b/Core.Inventory.Service.API/appsettings.Local.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "LocalGateways": { + "InventoryDAL": "https://localhost:7037" + } +} diff --git a/Core.Inventory.Service.API/appsettings.json b/Core.Inventory.Service.API/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/Core.Inventory.Service.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Core.Inventory.Service.sln b/Core.Inventory.Service.sln new file mode 100644 index 0000000..cf8ce1d --- /dev/null +++ b/Core.Inventory.Service.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36212.18 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Inventory.Service.API", "Core.Inventory.Service.API\Core.Inventory.Service.API.csproj", "{877AA831-C2E1-4926-AB6B-340ACCD88474}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Presentation", "Presentation", "{FAA84161-ED65-46CF-AC03-998CBDC8FCD3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Inventory.Application", "Core.Inventory.Application\Core.Inventory.Application.csproj", "{BA083BDD-F32A-4040-88F9-DC2DE767FB7A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Inventory.External", "Core.Inventory.External\Core.Inventory.External.csproj", "{C2C165F5-7787-4EE7-A335-31B59673BFCB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {877AA831-C2E1-4926-AB6B-340ACCD88474}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {877AA831-C2E1-4926-AB6B-340ACCD88474}.Debug|Any CPU.Build.0 = Debug|Any CPU + {877AA831-C2E1-4926-AB6B-340ACCD88474}.Release|Any CPU.ActiveCfg = Release|Any CPU + {877AA831-C2E1-4926-AB6B-340ACCD88474}.Release|Any CPU.Build.0 = Release|Any CPU + {BA083BDD-F32A-4040-88F9-DC2DE767FB7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA083BDD-F32A-4040-88F9-DC2DE767FB7A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA083BDD-F32A-4040-88F9-DC2DE767FB7A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA083BDD-F32A-4040-88F9-DC2DE767FB7A}.Release|Any CPU.Build.0 = Release|Any CPU + {C2C165F5-7787-4EE7-A335-31B59673BFCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2C165F5-7787-4EE7-A335-31B59673BFCB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2C165F5-7787-4EE7-A335-31B59673BFCB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2C165F5-7787-4EE7-A335-31B59673BFCB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {877AA831-C2E1-4926-AB6B-340ACCD88474} = {FAA84161-ED65-46CF-AC03-998CBDC8FCD3} + {BA083BDD-F32A-4040-88F9-DC2DE767FB7A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {C2C165F5-7787-4EE7-A335-31B59673BFCB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E943164B-39DF-481C-8CF1-A2B1D97F90BA} + EndGlobalSection +EndGlobal