feat(phase-0): finalize foundation — authorize, concurrency filter, isolated build

This commit is contained in:
Arthur Bassi 2026-06-30 13:20:41 -03:00
parent 7a81b6d279
commit 623810da45
4 changed files with 77 additions and 2 deletions

View file

@ -21,7 +21,7 @@ using SeaHaven.DataServices.Interfaces;
namespace Api.SeaHavenIndustries.Controllers
{
// [Authorize]
[Authorize]
[ApiController]
[Route("api/WorkOrder")]
[Route("api/workorders")]

View file

@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.EntityFrameworkCore;
namespace Api.SeaHavenIndustries.Filters
{
public class ConcurrencyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (context.Exception is not DbUpdateConcurrencyException) return;
context.Result = new ConflictObjectResult(new
{
status = "Conflict",
message = "The record was modified by another user. Refresh and retry.",
code = 409
});
context.ExceptionHandled = true;
}
}
}

View file

@ -22,7 +22,10 @@ builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddControllers().AddJsonOptions(options =>
builder.Services.AddControllers(options =>
{
options.Filters.Add<Api.SeaHavenIndustries.Filters.ConcurrencyExceptionFilter>();
}).AddJsonOptions(options =>
{
options.JsonSerializerOptions.MaxDepth = 264; // or any other desired value
});

View file

@ -0,0 +1,50 @@
using Api.SeaHavenIndustries.Filters;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
namespace SeaHavenIndustries.Tests;
public class ConcurrencyExceptionFilterTests
{
[Fact]
public void OnException_DbUpdateConcurrencyException_Returns409Conflict()
{
var filter = new ConcurrencyExceptionFilter();
var context = CreateExceptionContext(new DbUpdateConcurrencyException());
filter.OnException(context);
Assert.True(context.ExceptionHandled);
var result = Assert.IsType<ConflictObjectResult>(context.Result);
Assert.Equal(StatusCodes.Status409Conflict, result.StatusCode);
}
[Fact]
public void OnException_OtherException_DoesNotHandle()
{
var filter = new ConcurrencyExceptionFilter();
var context = CreateExceptionContext(new InvalidOperationException("other"));
filter.OnException(context);
Assert.False(context.ExceptionHandled);
Assert.Null(context.Result);
}
private static ExceptionContext CreateExceptionContext(Exception exception)
{
var actionContext = new ActionContext(
new DefaultHttpContext(),
new RouteData(),
new ActionDescriptor());
return new ExceptionContext(actionContext, [])
{
Exception = exception
};
}
}