First version of Service

This commit is contained in:
2025-06-22 19:34:07 -06:00
commit e99b2c5a5a
51 changed files with 2167 additions and 0 deletions

View File

@@ -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<GetFurnitureBaseByIdRequest> _getByIdHandler;
private readonly IComponentHandler<GetAllFurnitureBaseRequest> _getAllHandler;
private readonly IComponentHandler<CreateFurnitureBaseRequest> _createHandler;
private readonly IComponentHandler<UpdateFurnitureBaseRequest> _updateHandler;
private readonly IComponentHandler<ChangeFurnitureBaseStatusRequest> _changeStatusHandler;
private readonly IFurnitureBasePort _port;
public FurnitureBaseController(
IComponentHandler<GetFurnitureBaseByIdRequest> getByIdHandler,
IComponentHandler<GetAllFurnitureBaseRequest> getAllHandler,
IComponentHandler<CreateFurnitureBaseRequest> createHandler,
IComponentHandler<UpdateFurnitureBaseRequest> updateHandler,
IComponentHandler<ChangeFurnitureBaseStatusRequest> changeStatusHandler,
IFurnitureBasePort port)
{
_getByIdHandler= getByIdHandler;
_getAllHandler= getAllHandler;
_createHandler= createHandler;
_updateHandler= updateHandler;
_changeStatusHandler= changeStatusHandler;
_port= port;
}
[HttpGet("GetAll")]
public async Task<IActionResult> GetAllAsync(CancellationToken cancellationToken)
{
await _getAllHandler.ExecuteAsync(new GetAllFurnitureBaseRequest { }, cancellationToken).ConfigureAwait(false);
return _port.ViewModel;
}
[HttpPost("GetById")]
public async Task<IActionResult> 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<IActionResult> CreateAsync([FromBody] CreateFurnitureBaseRequest request, CancellationToken cancellationToken)
{
await _createHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return _port.ViewModel;
}
[HttpPut("Update")]
public async Task<IActionResult> UpdateAsync([FromBody] UpdateFurnitureBaseRequest request, CancellationToken cancellationToken)
{
await _updateHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return _port.ViewModel;
}
[HttpPatch("ChangeStatus")]
public async Task<IActionResult> 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;
}
}
}

View File

@@ -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<GetFurnitureVariantByIdRequest> getByIdHandler,
IComponentHandler<GetAllFurnitureVariantsByModelIdRequest> getAllHandler,
IComponentHandler<CreateFurnitureVariantRequest> createHandler,
IComponentHandler<UpdateFurnitureVariantRequest> updateHandler,
IComponentHandler<ChangeFurnitureVariantStatusRequest> changeStatusHandler,
IFurnitureVariantPort port) : ControllerBase
{
private readonly IComponentHandler<GetFurnitureVariantByIdRequest> _getByIdHandler = getByIdHandler;
private readonly IComponentHandler<GetAllFurnitureVariantsByModelIdRequest> _getAllHandler = getAllHandler;
private readonly IComponentHandler<CreateFurnitureVariantRequest> _createHandler = createHandler;
private readonly IComponentHandler<UpdateFurnitureVariantRequest> _updateHandler = updateHandler;
private readonly IComponentHandler<ChangeFurnitureVariantStatusRequest> _changeStatusHandler = changeStatusHandler;
private readonly IFurnitureVariantPort _port = port;
[HttpGet("GetAllByModelId")]
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> CreateAsync([FromBody] CreateFurnitureVariantRequest request, CancellationToken cancellationToken)
{
await _createHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return _port.ViewModel;
}
[HttpPut("Update")]
public async Task<IActionResult> UpdateAsync([FromBody] UpdateFurnitureVariantRequest request, CancellationToken cancellationToken)
{
await _updateHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return _port.ViewModel;
}
[HttpPatch("ChangeStatus")]
public async Task<IActionResult> 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;
}
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="Core.Blueprint.Logging" Version="1.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core.Inventory.Application\Core.Inventory.Application.csproj" />
<ProjectReference Include="..\Core.Inventory.External\Core.Inventory.External.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
@Core.Inventory.Service.API_HostAddress = http://localhost:5257
GET {{Core.Inventory.Service.API_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -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<IFurnitureBasePort, FurnitureBasePort>();
services.AddScoped<IComponentHandler<GetAllFurnitureBaseRequest>, InventoryHandler>();
services.AddScoped<IComponentHandler<GetFurnitureBaseByIdRequest>, InventoryHandler>();
services.AddScoped<IComponentHandler<CreateFurnitureBaseRequest>, InventoryHandler>();
services.AddScoped<IComponentHandler<UpdateFurnitureBaseRequest>, InventoryHandler>();
services.AddScoped<IComponentHandler<ChangeFurnitureBaseStatusRequest>, InventoryHandler>();
services.AddValidatorsFromAssemblyContaining<CreateFurnitureBaseValidator>();
services.AddScoped<IValidator<CreateFurnitureBaseRequest>, CreateFurnitureBaseValidator>();
services.AddValidatorsFromAssemblyContaining<UpdateFurnitureBaseValidator>();
services.AddScoped<IValidator<UpdateFurnitureBaseRequest>, UpdateFurnitureBaseValidator>();
services.AddValidatorsFromAssemblyContaining<ChangeFurnitureBaseStatusValidator>();
services.AddScoped<IValidator<ChangeFurnitureBaseStatusRequest>, ChangeFurnitureBaseStatusValidator>();
services.AddValidatorsFromAssemblyContaining<GetFurnitureBaseByIdValidator>();
services.AddScoped<IValidator<GetFurnitureBaseByIdRequest>, GetFurnitureBaseByIdValidator>();
#endregion
#region FurnitureVariant
services.AddScoped<IFurnitureVariantPort, FurnitureVariantPort>();
services.AddScoped<IComponentHandler<GetAllFurnitureVariantsByModelIdRequest>, InventoryHandler>();
services.AddScoped<IComponentHandler<GetFurnitureVariantByIdRequest>, InventoryHandler>();
services.AddScoped<IComponentHandler<CreateFurnitureVariantRequest>, InventoryHandler>();
services.AddScoped<IComponentHandler<UpdateFurnitureVariantRequest>, InventoryHandler>();
services.AddScoped<IComponentHandler<ChangeFurnitureVariantStatusRequest>, InventoryHandler>();
services.AddValidatorsFromAssemblyContaining<CreateFurnitureVariantValidator>();
services.AddScoped<IValidator<CreateFurnitureVariantRequest>, CreateFurnitureVariantValidator>();
services.AddValidatorsFromAssemblyContaining<UpdateFurnitureVariantValidator>();
services.AddScoped<IValidator<UpdateFurnitureVariantRequest>, UpdateFurnitureVariantValidator>();
services.AddValidatorsFromAssemblyContaining<ChangeFurnitureVariantStatusValidator>();
services.AddScoped<IValidator<ChangeFurnitureVariantStatusRequest>, ChangeFurnitureVariantStatusValidator>();
services.AddValidatorsFromAssemblyContaining<GetFurnitureVariantByIdValidator>();
services.AddScoped<IValidator<GetFurnitureVariantByIdRequest>, GetFurnitureVariantByIdValidator>();
services.AddValidatorsFromAssemblyContaining<GetAllFurnitureVariantsByModelIdValidator>();
services.AddScoped<IValidator<GetAllFurnitureVariantsByModelIdRequest>, GetAllFurnitureVariantsByModelIdValidator>();
#endregion
return services;
}
}
}

View File

@@ -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<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
}
/// <summary>
/// Configures Swagger generation with OAuth2 security and XML comments.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="configuration">The <see cref="IConfiguration"/> containing Swagger and OAuth2 configuration settings.</param>
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<string>()
}
});
});
}
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<SwaggerGenOptions>
{
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("+", "."));
}
}
}

View File

@@ -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<Microsoft.AspNetCore.Http.Json.JsonOptions>(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();

View File

@@ -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"
}
}
}
}

View File

@@ -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; }
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"LocalGateways": {
"InventoryDAL": "https://localhost:7037"
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}