Cayaqui.MPS.ApiClient 0.1.0

dotnet add package Cayaqui.MPS.ApiClient --version 0.1.0
                    
NuGet\Install-Package Cayaqui.MPS.ApiClient -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cayaqui.MPS.ApiClient" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cayaqui.MPS.ApiClient" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Cayaqui.MPS.ApiClient" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Cayaqui.MPS.ApiClient --version 0.1.0
                    
#r "nuget: Cayaqui.MPS.ApiClient, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Cayaqui.MPS.ApiClient@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Cayaqui.MPS.ApiClient&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Cayaqui.MPS.ApiClient&version=0.1.0
                    
Install as a Cake Tool

Cayaqui.MPS.ApiClient

Cliente HTTP tipado y Result-based para consumir APIs MPS desde Blazor / MAUI / cualquier .NET. Espeja el contrato de errores del companion server: el API serializa DomainError→ProblemDetails; este paquete deserializa ProblemDetails→Result<T>.Failure(DomainError). Nunca lanza por error de negocio — todo 4xx/5xx con ProblemDetails se mapea a Result<T> Failure.

Distribución propietaria — requiere contrato comercial con Cayaqui. Ver LICENSE.txt.

Instalación

<PackageReference Include="Cayaqui.MPS.ApiClient" Version="0.1.0" />

Depende de Cayaqui.MPS.BuildingBlocks ≥ 0.4.0 (Result<T>, DomainError, ErrorType, Unit). Sin dependencia ASP.NET (cliente puro).

Servicio tipado

Todos los servicios API implementan el marker IBaseApiService (regla global Cayaqui) y heredan de BaseApiService, que expone verb-helpers que devuelven Result<T>:

using MPS.ApiClient;
using MPS.BuildingBlocks.Application;   // Result<T>, Unit

public interface INotificationsApiService : IBaseApiService
{
    Task<Result<PagedResult<NotificationDto>>> GetMineAsync(bool unreadOnly, int page, int pageSize, CancellationToken ct);
    Task<Result<Unit>> MarkAsReadAsync(MarkAsReadRequest req, CancellationToken ct);
}

internal sealed class NotificationsApiService(HttpClient http)
    : BaseApiService(http), INotificationsApiService
{
    public Task<Result<PagedResult<NotificationDto>>> GetMineAsync(bool unreadOnly, int page, int pageSize, CancellationToken ct) =>
        GetAsync<PagedResult<NotificationDto>>($"api/notifications?unreadOnly={unreadOnly}&page={page}&pageSize={pageSize}", ct);

    public Task<Result<Unit>> MarkAsReadAsync(MarkAsReadRequest req, CancellationToken ct) =>
        PostAsync("api/notifications/mark-as-read", req, ct);   // → Result<Unit>
}

Verb-helpers (protected en BaseApiService)

Helper Devuelve
GetAsync<T>(uri, ct) Result<T>
PostAsync<TReq,T>(uri, body, ct) / PostAsync<TReq>(uri, body, ct) Result<T> / Result<Unit>
PutAsync<TReq,T>(uri, body, ct) / PutAsync<TReq>(uri, body, ct) Result<T> / Result<Unit>
DeleteAsync(uri, ct) / DeleteAsync<T>(uri, ct) Result<Unit> / Result<T>

Comportamiento del núcleo:

  • 2xx con bodyResult.Success. Body JSON inválido o nullFailure(Unexpected) (deserialization_error / empty_response).
  • 204 / vacío en overload UnitSuccess(Unit.Value).
  • 4xx/5xx → deserializa ProblemDetails → Result.Failure(DomainError) (Type/Code/Message/Metadata recuperados).
  • Transporte / timeoutFailure(Unexpected, "transport_error"). La cancelación real del CancellationToken del llamador propaga.
  • JSON: default web (camelCase, case-insensitive) + enums como string. Deben coincidir con las options del API. Overridable por ctor.

Consumo del resultado

var result = await notifications.GetMineAsync(unreadOnly: true, 1, 20, ct);
if (result.IsSuccess)
    Render(result.Value);
else
    ShowError(result.Error.Message);   // result.Error : DomainError (Code, Message, Type, Metadata)

Autenticación

El paquete provee IAccessTokenProvider (lo implementa la app) + BearerTokenHandler:

services.AddMpsApiClientCore();                              // registra BearerTokenHandler
services.AddMpsAccessTokenProvider<OboAccessTokenProvider>();// tu impl
services.AddHttpClient<INotificationsApiService, NotificationsApiService>(c => c.BaseAddress = apiBaseUrl)
        .AddHttpMessageHandler<BearerTokenHandler>()
        .AddStandardResilienceHandler();                    // Polly — decisión tuya, no del paquete

⚠ Contrato del provider. IHttpClientFactory poolea los handlers (~2 min), así que la instancia de IAccessTokenProvider se reusa entre requests/usuarios. Tu impl debe leer el usuario actual en cada GetAccessTokenAsync (fuente context-dynamic), no cachear en campos.

  • API/MVC/Razor Pages: ITokenAcquisition.GetAccessTokenForUserAsync funciona — IHttpContextAccessor es AsyncLocal.
  • MAUI: MSAL AcquireTokenSilent/Interactive (PublicClientApplication singleton).
  • 🔴 Blazor Server (InteractiveServer): las llamadas desde componentes interactivos corren en el circuit sin HttpContext → OBO vía ITokenAcquisition falla, y el handler pooleado no ve el circuit. Obtené el token por un mecanismo circuit-safe: adquirirlo en el servicio tipado (circuit scope) y attachearlo per-call, un provider sobre AuthenticationStateProvider + token store server-side, o Microsoft.Identity.Web.IDownstreamApi.

Contrato de errores (round-trip)

Simétrico a MPS.BuildingBlocks.AspNetCore.ProblemDetailsMapper (garantizado por test):

DomainError ProblemDetails
Type (ErrorType) Title (= Type.ToString()); fallback por status code
Code Extensions["code"]; fallback http_{status}
Message Detail; fallback Title / reason phrase
Metadata Extensions["errors"]

ErrorType: Validation(400) · Unauthorized(401) · Forbidden(403) · NotFound(404) · Conflict/Concurrency(409) · Unexpected(500).

Versiones

v0.1.0 — Release inicial

Marker IBaseApiService, BaseApiService (verb-helpers Result-based, nunca lanza), mapeo inverso ProblemDetails→Result<DomainError>, IAccessTokenProvider + BearerTokenHandler, DI mínimo (AddMpsApiClientCore, AddMpsAccessTokenProvider<T>). Depende de Cayaqui.MPS.BuildingBlocks 0.4.0.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 99 6/4/2026

0.1.0 — Release inicial. Cliente HTTP tipado Result-based: marker `IBaseApiService` + `BaseApiService` (verb-helpers Get/Post/Put/Delete → `Result<T>`, nunca lanza por error de negocio), mapeo inverso ProblemDetails→`Result<DomainError>` simétrico a `MPS.BuildingBlocks.AspNetCore.ProblemDetailsMapper`, `IAccessTokenProvider` + `BearerTokenHandler` para auth reutilizable (Web OBO / MAUI MSAL), DI mínimo (`AddMpsApiClientCore`, `AddMpsAccessTokenProvider`). Sin dependencia ASP.NET. Depende de Cayaqui.MPS.BuildingBlocks 0.4.0.