using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.Extensions.Options; var builder = WebApplication.CreateBuilder(args); builder.Services.AddValidation(); builder.Services.AddProblemDetails(); builder.Services.AddOpenApi(); builder.Services.AddOptions().Bind(builder.Configuration.GetSection("Quote")).Validate(x => x.MaximumQuantity is >= 1 and <= 10, "MaximumQuantity must be 1 through 10").ValidateOnStart(); builder.Services.AddSingleton(); builder.Services.AddScoped(); var app = builder.Build(); app.UseExceptionHandler(); if (app.Environment.IsDevelopment()) app.MapOpenApi(); app.MapPost("/quotes", Results, NotFound, ProblemHttpResult> (QuoteRequest request, QuoteService service, CancellationToken token) => service.Create(request, token)).ProducesValidationProblem().ProducesProblem(StatusCodes.Status409Conflict).WithName("CreateQuote"); app.Run(); public sealed class QuoteRequest { [Required, MinLength(2)] public string Item { get; set; } = ""; [Range(1, 10)] public int Quantity { get; set; } } public sealed class QuoteLimits { public int MaximumQuantity { get; set; } } public sealed record Receipt(string Item, int Quantity, decimal Total); public sealed class Catalog { public bool TryPrice(string name, out decimal price) { price = 12.5m; return string.Equals(name, "rune", StringComparison.Ordinal); } } public sealed class QuoteService(Catalog catalog, IOptions options, ILogger logger) { public Results, NotFound, ProblemHttpResult> Create(QuoteRequest request, CancellationToken token) { token.ThrowIfCancellationRequested(); if (!catalog.TryPrice(request.Item, out decimal price)) return TypedResults.NotFound(); if (request.Quantity > options.Value.MaximumQuantity) return TypedResults.Problem(statusCode: 409, title: "Configured quantity limit exceeded"); decimal total = checked(price * request.Quantity); logger.LogInformation("Quoted {Quantity} units of {CatalogItem}", request.Quantity, request.Item); return TypedResults.Ok(new Receipt(request.Item, request.Quantity, total)); } }