diff --git a/src/PostIt/PostIt/PostIt.csproj b/src/PostIt/PostIt/PostIt.csproj index d9cf96d3..e4d51a88 100644 --- a/src/PostIt/PostIt/PostIt.csproj +++ b/src/PostIt/PostIt/PostIt.csproj @@ -25,6 +25,7 @@ + diff --git a/src/PostIt/PostIt/Services/YavscApiClient.cs b/src/PostIt/PostIt/Services/YavscApiClient.cs index 9ae1453b..b611fe02 100644 --- a/src/PostIt/PostIt/Services/YavscApiClient.cs +++ b/src/PostIt/PostIt/Services/YavscApiClient.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using IdentityModel.OidcClient; using PostIt.ViewModels; +using Yavsc.Api.Client; namespace PostIt.Services; @@ -24,7 +25,7 @@ namespace PostIt.Services; /// only refreshes once even if many /// concurrent requests are in flight. /// -public class YavscApiClient : IAsyncDisposable +public class YavscApiClient : IYavscApiClient, IAsyncDisposable { // 60s of slack before the access_token's nominal expiry. Covers // network latency + JWT validation on the server side. diff --git a/src/Yavsc.Api.Client/IYavscApiClient.cs b/src/Yavsc.Api.Client/IYavscApiClient.cs new file mode 100644 index 00000000..209ec07d --- /dev/null +++ b/src/Yavsc.Api.Client/IYavscApiClient.cs @@ -0,0 +1,62 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Yavsc.Api.Client; + +/// +/// Transport surface that the high-level clients +/// (, , +/// ) need to do their work. +/// +/// This is intentionally a thin, transport-only contract. It +/// does not include the OIDC login / refresh / logout surface — +/// that lives on the concrete YavscApiClient in the +/// consuming application and is wired by the application +/// composition root. Splitting the two keeps Yavsc.Api.Client +/// usable from any host (a CLI, a unit test, a future iOS +/// client) without dragging OIDC, identity, and a Settings +/// POMVO everywhere. +/// +/// Implementations are expected to: +/// +/// Attach a Bearer access token to every outbound request. +/// Silently refresh the token on a 401 and retry once. +/// Serialise the request body as JSON and deserialise the +/// response body with case-insensitive property matching. +/// +/// +/// The exception contract on non-2xx responses is +/// with a message that includes +/// the response body (capped), so callers can surface the +/// server-side validation problem to the UI without losing +/// context. +/// +public interface IYavscApiClient : IAsyncDisposable +{ + /// + /// The configured . Clients set its + /// BaseAddress in their constructors to point at the + /// API host they target. + /// + HttpClient Http { get; } + + /// Call a JSON endpoint with a typed return value. + /// HTTP verb. + /// Path relative to . + /// Optional request body, serialised as JSON. + /// Cancellation token. + Task CallAsync( + HttpMethod method, + string path, + object? body = null, + CancellationToken ct = default); + + /// Call a JSON endpoint that returns no useful body (DELETE, 204, etc.). + Task CallAsync( + HttpMethod method, + string path, + object? body = null, + CancellationToken ct = default); +} diff --git a/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj b/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj new file mode 100644 index 00000000..5376856d --- /dev/null +++ b/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj @@ -0,0 +1,29 @@ + + + net10.0 + enable + Yavsc.Api.Client + Yavsc.Api.Client + enable + latest + true + + Thin HTTP clients for the Yavsc API. Each client is a DTO↔path + mapper; all transport concerns (base URL, JSON, Bearer auth, + silent refresh on 401) are delegated to YavscApiClient, which + lives in the consuming application (PostIt). + + https://github.com/pazof/yavsc + true + 1.0.1.0 + 1.0.1.0 + 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f + 1.0.1-5 + + + + + + + +