Repository navigation
Expand file tree
/
Copy pathProgram.cs
More file actions
49 lines (41 loc) · 3.27 KB
/
Copy pathProgram.cs
File metadata and controls
49 lines (41 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// A minimal API over the NBP clients registered with OpenUrzednik.Nbp.DependencyInjection.
using OpenUrzednik.Core;
using OpenUrzednik.Core.Errors;
using OpenUrzednik.Nbp;
var builder = WebApplication.CreateBuilder(args);
// Registers the three NBP clients on one named HttpClient; logs go to the app's ILoggerFactory.
// The standard resilience handler (Microsoft.Extensions.Http.Resilience) adds retries, timeouts and a circuit breaker;
// leave NbpOptions.Timeout unset with it, or that deadline cuts the retries short.
builder.Services.AddOpenUrzednikNbp()
.AddStandardResilienceHandler();
var app = builder.Build();
app.MapGet("/gold/latest", async (INbpGoldPriceClient gold, CancellationToken cancellationToken)
=> ToHttpResult(await gold.GetLatestAsync(cancellationToken)));
// The query parameter has the client parameter's name, so validation errors name it (see ToHttpResult).
app.MapGet("/rates/{currency}", async (string currency, int? topCount, INbpCurrencyExchangeRateClient rates, CancellationToken cancellationToken)
=> ToHttpResult(await rates.GetTopCountAsync(currency, topCount ?? 1, cancellationToken: cancellationToken)));
app.MapGet("/tables/c/latest", async (INbpExchangeRateTableClient tables, CancellationToken cancellationToken)
=> ToHttpResult(await tables.GetBuySellLatestAsync(cancellationToken)));
app.Run();
// Maps a result to an HTTP response: the value as 200, each error type to a matching status with a problem details body.
// The messages describe the request to NBP; in production, consider not passing upstream details to your callers.
static IResult ToHttpResult<T>(OpenUrzednikResult<T> result)
=> result.Match(
value => Results.Ok(value),
errors => errors[0] switch
{
// A ValidationError's name metadata is the client method's parameter name (e.g. "topCount").
ValidationError => Results.ValidationProblem(errors
.GroupBy(e => e.Metadata.TryGetValue(ValidationError.NameMetadataKey, out var name) ? name?.ToString() ?? "" : "")
.ToDictionary(g => g.Key, g => g.Select(e => e.Message).ToArray())),
NotFoundError error => Results.Problem(error.Message, statusCode: StatusCodes.Status404NotFound),
// The resilience handler has already retried, honoring Retry-After; RateLimitExceededError.RetryAfter has the last value.
RateLimitExceededError error => Results.Problem(error.Message, statusCode: StatusCodes.Status429TooManyRequests),
RequestTimeoutError error => Results.Problem(error.Message, statusCode: StatusCodes.Status504GatewayTimeout),
// NBP answered 5xx, or the handler's circuit breaker or rate limiter rejected the request.
ServiceUnavailableError error => Results.Problem(error.Message, statusCode: StatusCodes.Status503ServiceUnavailable),
// NBP couldn't be reached (DNS, TLS, connection) or the connection broke while reading the response.
ConnectionFailedError error => Results.Problem(error.Message, statusCode: StatusCodes.Status502BadGateway),
// Anything else (a bad response, an unexpected status) failed between this app and NBP, not because of the caller.
var error => Results.Problem(error.Message, statusCode: StatusCodes.Status502BadGateway),
});