First version of DAL

This commit is contained in:
2025-06-22 04:38:36 -06:00
commit 658e3f4277
23 changed files with 1538 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
// ***********************************************************************
// <copyright file="FurnitureBaseController.cs">
// Core.Inventory
// </copyright>
// ***********************************************************************
using Asp.Versioning;
using Core.Adapters.Lib;
using Core.Blueprint.Logging;
using Core.Blueprint.Mongo;
using Core.Inventory.Domain.Contexts.Inventory.Request;
using Core.Inventory.Provider.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Core.Inventory.DAL.API.Controllers
{
/// <summary>
/// Handles all requests for furniture base CRUD operations.
/// </summary>
[ApiVersion(MimeTypes.ApplicationVersion)]
[Route("api/v{api-version:apiVersion}/[controller]")]
[Produces(MimeTypes.ApplicationJson)]
[Consumes(MimeTypes.ApplicationJson)]
[ApiController]
[AllowAnonymous]
public class FurnitureBaseController(IFurnitureBaseProvider service) : ControllerBase
{
/// <summary>
/// Gets all furniture base records.
/// </summary>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<FurnitureBase>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAllAsync(CancellationToken cancellationToken)
{
var result = await service.GetAllAsync(cancellationToken);
return Ok(result);
}
/// <summary>
/// Gets a furniture base record by ID.
/// </summary>
[HttpGet]
[Route("{id}")]
[ProducesResponseType(typeof(FurnitureBase), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetByIdAsync([FromRoute] string id, CancellationToken cancellationToken)
{
var result = await service.GetByIdAsync(id, cancellationToken);
return result is not null ? Ok(result) : NotFound("Entity not found");
}
/// <summary>
/// Creates a new furniture base record.
/// </summary>
[HttpPost]
[ProducesResponseType(typeof(FurnitureBase), StatusCodes.Status201Created)]
public async Task<IActionResult> CreateAsync([FromBody] FurnitureBaseRequest request, CancellationToken cancellationToken)
{
var result = await service.CreateAsync(request, cancellationToken);
return Created("CreatedWithIdAsync", result);
}
/// <summary>
/// Updates a furniture base record by ID.
/// </summary>
[HttpPut]
[Route("{id}")]
[ProducesResponseType(typeof(FurnitureBase), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdateAsync([FromRoute] string id, [FromBody] FurnitureBase entity, CancellationToken cancellationToken)
{
if (id != entity.Id?.ToString())
return BadRequest("Furniture ID mismatch");
var result = await service.UpdateAsync(id, entity, cancellationToken);
return Ok(result);
}
/// <summary>
/// Changes the status of a furniture base record.
/// </summary>
[HttpPatch]
[Route("{id}/{newStatus}/ChangeStatus")]
[ProducesResponseType(typeof(FurnitureBase), StatusCodes.Status200OK)]
public async Task<IActionResult> ChangeStatusAsync([FromRoute] string id, [FromRoute] StatusEnum newStatus, CancellationToken cancellationToken)
{
var result = await service.ChangeStatusAsync(id, newStatus, cancellationToken);
return Ok(result);
}
}
}

View File

@@ -0,0 +1,97 @@
// ***********************************************************************
// <copyright file="FurnitureVariantController.cs">
// Core.Inventory
// </copyright>
// ***********************************************************************
using Asp.Versioning;
using Core.Adapters.Lib;
using Core.Blueprint.Logging;
using Core.Blueprint.Mongo;
using Core.Inventory.Domain.Contexts.Inventory.Request;
using Core.Inventory.Provider.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Core.Inventory.DAL.API.Controllers
{
/// <summary>
/// Handles all requests for furniture variant CRUD operations.
/// </summary>
[ApiVersion(MimeTypes.ApplicationVersion)]
[Route("api/v{api-version:apiVersion}/[controller]")]
[Produces(MimeTypes.ApplicationJson)]
[Consumes(MimeTypes.ApplicationJson)]
[ApiController]
[AllowAnonymous]
public class FurnitureVariantController(IFurnitureVariantProvider service) : ControllerBase
{
/// <summary>
/// Gets all furniture variant records.
/// </summary>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<FurnitureVariant>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAllVariantsByModelIdAsync([FromRoute] string modelId, CancellationToken cancellationToken)
{
var result = await service.GetAllByModelIdAsync(modelId, cancellationToken).ConfigureAwait(false);
if (result is null || !result.Any())
{
return NotFound($"No variants found for base model with ID '{modelId}'.");
}
return Ok(result);
}
/// <summary>
/// Gets a furniture variant record by ID.
/// </summary>
[HttpGet]
[Route("{id}")]
[ProducesResponseType(typeof(FurnitureVariant), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetByIdAsync([FromRoute] string id, CancellationToken cancellationToken)
{
var result = await service.GetByIdAsync(id, cancellationToken);
return result is not null ? Ok(result) : NotFound("Entity not found");
}
/// <summary>
/// Creates a new furniture variant.
/// </summary>
[HttpPost]
[ProducesResponseType(typeof(FurnitureVariant), StatusCodes.Status201Created)]
public async Task<IActionResult> CreateAsync([FromBody] FurnitureVariantRequest request, CancellationToken cancellationToken)
{
var result = await service.CreateAsync(request, cancellationToken);
return Created("CreatedWithIdAsync", result);
}
/// <summary>
/// Updates a furniture variant record by ID.
/// </summary>
[HttpPut]
[Route("{id}")]
[ProducesResponseType(typeof(FurnitureVariant), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdateAsync([FromRoute] string id, [FromBody] FurnitureVariant entity, CancellationToken cancellationToken)
{
if (id != entity.Id?.ToString())
return BadRequest("Furniture Variant ID mismatch");
var result = await service.UpdateAsync(id, entity, cancellationToken);
return Ok(result);
}
/// <summary>
/// Changes the status of a furniture variant record.
/// </summary>
[HttpPatch]
[Route("{id}/{newStatus}/ChangeStatus")]
[ProducesResponseType(typeof(FurnitureVariant), StatusCodes.Status200OK)]
public async Task<IActionResult> ChangeStatusAsync([FromRoute] string id, [FromRoute] StatusEnum newStatus, CancellationToken cancellationToken)
{
var result = await service.ChangeStatusAsync(id, newStatus, cancellationToken);
return Ok(result);
}
}
}

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.Domain\Core.Inventory.Domain.csproj" />
<ProjectReference Include="..\Core.Inventory.Provider\Core.Inventory.Provider.csproj" />
</ItemGroup>
</Project>

View File

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

View File

@@ -0,0 +1,66 @@
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Any;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
namespace Core.Inventory.DAL.API.Extensions
{
public static class SwaggerExtensions
{
public static void AddSwagger(this IServiceCollection services)
{
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
}
public static void ConfigureSwagger(this WebApplication app)
{
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.MapType<DateOnly>(() => new()
{
Format= "date",
Example = new OpenApiString(DateOnly.MinValue.ToString())
});
options.CustomSchemaIds(type => type.ToString().Replace("+", "."));
}
}
}
}

View File

@@ -0,0 +1,81 @@
using Core.Blueprint.DAL.Mongo.Configuration;
using Core.Blueprint.Logging.Configuration;
using Core.Blueprint.Redis.Configuration;
using Core.Inventory.DAL.API.Extensions;
using Core.Inventory.Provider;
using Microsoft.AspNetCore.HttpLogging;
using System.Reflection;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Configuration
.AddUserSecrets(Assembly.GetExecutingAssembly())
.AddEnvironmentVariables();
builder.Services.AddResponseCompression();
builder.Services.AddProblemDetails();
builder.Services.AddMemoryCache();
builder.Services.AddLogs(builder);
builder.Services.AddRedis(builder.Configuration);
builder.Services.AddMongoLayer(builder.Configuration);
builder.Services.AddDALLayerServices(builder.Configuration);
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();
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.UseSwagger();
app.UseSwaggerUI();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.UseCors();
app.ConfigureSwagger();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
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:16111",
"sslPort": 44378
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5069",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Local"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7037;http://localhost:5069",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Local"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Local"
}
}
}
}

View File

@@ -0,0 +1,22 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MongoDB": "mongodb://localhost:27017",
"Redis": "localhost:6379"
},
"MongoDb": {
"DatabaseName": "Inventory",
"LocalAudience": ""
},
"DetailedErrors": true,
"UseRedisCache": true,
"CacheSettings": {
"DefaultCacheDurationInMinutes": 3
}
}

View File

@@ -0,0 +1,26 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MongoDB": "mongodb://admin_agile:Admin%40agileWebs@portainer.white-enciso.pro:27017/?authMechanism=SCRAM-SHA-256",
"Redis": "100.123.31.103:6379"
},
"MongoDb": {
"DatabaseName": "Inventory",
"LocalAudience": ""
},
"DetailedErrors": true,
"UseRedisCache": true,
"CacheSettings": {
"DefaultCacheDurationInMinutes": 3
},
"ServiceSettings": {
"ApplicationName": "inventory",
"LayerName": "dal"
}
}

View File

@@ -0,0 +1,22 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MongoDB": "mongodb://admin_agile:Admin%40agileWebs@portainer.white-enciso.pro:27017/?authMechanism=SCRAM-SHA-256",
"Redis": "localhost:6379"
},
"MongoDb": {
"DatabaseName": "Inventory",
"LocalAudience": ""
},
"DetailedErrors": true,
"UseRedisCache": true,
"CacheSettings": {
"DefaultCacheDurationInMinutes": 3
}
}

View File

@@ -0,0 +1,35 @@
{
"ConnectionStrings": {
"DefaultConnection": "", // Sandbox MongoDB connection string
"Redis": "", // New Redis connection string
"KeyVault": "" //KeyVault Uri
},
"MongoDb": {
"DatabaseName": "Inventory"
},
"CacheSettings": {
"DefaultCacheDurationInMinutes": 3 // Default cache duration set to 3 minutes
},
"JwtIssuerOptions": {
"Audience": "", // Audience for token creation, specifies intended recipients
"Issuer": "" // Issuer for token creation, identifies the issuer of the token
},
"AzureAdB2C": {
"Instance": "", // Azure AD instance URL (STORED IN KEY VAULT)
"TenantId": "", // Azure AD tenant ID (STORED IN KEY VAULT)
"ClientId": "", // Azure AD application client ID (STORED IN KEY VAULT)
"ClientSecret": "", // Azure AD application client secret (STORED IN KEY VAULT)
"CallbackPath": "", // Path for redirect after authentication
"Scopes": "" // Access scopes for user permissions
},
"InventoryApp": {
"AuthorizationUrl": "", // URL for authorization endpoint (STORED IN KEY VAULT)
"TokenUrl": "", // URL for token endpoint (STORED IN KEY VAULT)
"Scope": "", // Scope for application permissions (STORED IN KEY VAULT)
"ClientId": "" // Client ID for Inventory application (STORED IN KEY VAULT)
},
"MicrosoftGraph": {
"Scopes": "", // Scopes for Microsoft Graph API access
"BaseUrl": "" // Base URL for Microsoft Graph API
}
}