117 lines
4.3 KiB
C#
117 lines
4.3 KiB
C#
// ----------------------------------------------------------------------------
|
|
// UpdateOrderStatusEndpoint
|
|
//
|
|
// Demonstrates:
|
|
// - PATCH operation for partial updates
|
|
// - Path parameters with request body
|
|
// - Write authorization claims
|
|
// - State machine validation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
using Examples.OrderService.Models;
|
|
using Microsoft.Extensions.Logging;
|
|
using StellaOps.Microservice;
|
|
|
|
namespace Examples.OrderService.Endpoints;
|
|
|
|
/// <summary>
|
|
/// Request to update order status.
|
|
/// </summary>
|
|
public sealed record UpdateOrderStatusRequest
|
|
{
|
|
public required string OrderId { get; init; }
|
|
public required OrderStatus NewStatus { get; init; }
|
|
public string? Reason { get; init; }
|
|
public string? TrackingNumber { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Response after status update.
|
|
/// </summary>
|
|
public sealed record UpdateOrderStatusResponse
|
|
{
|
|
public required string OrderId { get; init; }
|
|
public required OrderStatus PreviousStatus { get; init; }
|
|
public required OrderStatus CurrentStatus { get; init; }
|
|
public required DateTimeOffset UpdatedAt { get; init; }
|
|
public string? Message { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Endpoint for updating order status with state machine validation.
|
|
/// </summary>
|
|
[StellaEndpoint("PATCH", "/orders/{id}/status", TimeoutSeconds = 10, RequiredClaims = ["orders:write"])]
|
|
[ValidateSchema(
|
|
ValidateRequest = true,
|
|
ValidateResponse = true,
|
|
Summary = "Update order status",
|
|
Description = "Updates the status of an order. Status transitions are validated against allowed state machine rules.",
|
|
Tags = ["orders", "write", "status"])]
|
|
public sealed class UpdateOrderStatusEndpoint : IStellaEndpoint<UpdateOrderStatusRequest, UpdateOrderStatusResponse>
|
|
{
|
|
private readonly ILogger<UpdateOrderStatusEndpoint> _logger;
|
|
|
|
// Valid state transitions
|
|
private static readonly Dictionary<OrderStatus, OrderStatus[]> ValidTransitions = new()
|
|
{
|
|
[OrderStatus.Pending] = [OrderStatus.Confirmed, OrderStatus.Cancelled],
|
|
[OrderStatus.Confirmed] = [OrderStatus.Processing, OrderStatus.Cancelled],
|
|
[OrderStatus.Processing] = [OrderStatus.Shipped, OrderStatus.Cancelled],
|
|
[OrderStatus.Shipped] = [OrderStatus.Delivered],
|
|
[OrderStatus.Delivered] = [OrderStatus.Refunded],
|
|
[OrderStatus.Cancelled] = [],
|
|
[OrderStatus.Refunded] = []
|
|
};
|
|
|
|
public UpdateOrderStatusEndpoint(ILogger<UpdateOrderStatusEndpoint> logger)
|
|
{
|
|
_logger = logger;
|
|
}
|
|
|
|
public Task<UpdateOrderStatusResponse> HandleAsync(
|
|
UpdateOrderStatusRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
// Simulate current status lookup
|
|
var currentStatus = OrderStatus.Processing;
|
|
|
|
_logger.LogInformation(
|
|
"Updating order {OrderId} status: {CurrentStatus} -> {NewStatus}",
|
|
request.OrderId, currentStatus, request.NewStatus);
|
|
|
|
// Validate state transition
|
|
if (!IsValidTransition(currentStatus, request.NewStatus))
|
|
{
|
|
_logger.LogWarning(
|
|
"Invalid status transition for order {OrderId}: {Current} -> {New}",
|
|
request.OrderId, currentStatus, request.NewStatus);
|
|
|
|
// In real implementation, throw an exception that maps to 422
|
|
return Task.FromResult(new UpdateOrderStatusResponse
|
|
{
|
|
OrderId = request.OrderId,
|
|
PreviousStatus = currentStatus,
|
|
CurrentStatus = currentStatus, // Unchanged
|
|
UpdatedAt = DateTimeOffset.UtcNow,
|
|
Message = $"Invalid transition: {currentStatus} cannot transition to {request.NewStatus}"
|
|
});
|
|
}
|
|
|
|
return Task.FromResult(new UpdateOrderStatusResponse
|
|
{
|
|
OrderId = request.OrderId,
|
|
PreviousStatus = currentStatus,
|
|
CurrentStatus = request.NewStatus,
|
|
UpdatedAt = DateTimeOffset.UtcNow,
|
|
Message = request.NewStatus == OrderStatus.Shipped
|
|
? $"Shipped with tracking: {request.TrackingNumber}"
|
|
: null
|
|
});
|
|
}
|
|
|
|
private static bool IsValidTransition(OrderStatus current, OrderStatus target)
|
|
{
|
|
return ValidTransitions.TryGetValue(current, out var allowed) && allowed.Contains(target);
|
|
}
|
|
}
|