feat(api-client): add Yavsc.Api.Client with Blog + Circle + BlogAcl clients
Creates the high-level HTTP client library the PostIt UI will consume to manage blog posts, circles, and per-post ACLs. Clients in this commit: - BlogApiClient (moved from PostIt/Services; same public surface, now depends on IYavscApiClient instead of the concrete class). - CircleApiClient (new): GET/POST/PUT/DELETE /api/circle. Takes the blogs base URL explicitly in its constructor so it doesn't need to know about PostIt's Settings type. - BlogAclApiClient (new): GET/POST/PUT/DELETE /api/blogacl. Same conventions as CircleApiClient. DTOs (Yavsc.Api.Client.Dtos): - CircleDto: id, name, ownerId, public. Stops short of the navigation properties on the server-side Circle (Owner, Members), which depend on ApplicationUser and other server types we don't want to drag into the client. - CircleAuthorizationDto: circleId, blogPostId, comment. Same reason: the server entity has Target and Allowed navigation properties the client never needs. The clients now require the caller to pass the blogs base URL explicitly in the constructor (previously the BlogApiClient sniffed it off YavscApiClient.Settings.BlogsApiUrl, but that field is PostIt-specific). The one production call site (App.axaml.cs) and four test call sites are updated to pass the URL. Build + 51/51 tests green. The IYavscApiClient abstraction was landed in the previous commit so this one could be a pure addition + relocation.
This commit is contained in:
parent
ab40af8ef1
commit
f835ad42a1
11 changed files with 171 additions and 12 deletions
53
src/Yavsc.Api.Client/CircleApiClient.cs
Normal file
53
src/Yavsc.Api.Client/CircleApiClient.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Yavsc.Api.Client.Dtos;
|
||||
|
||||
namespace Yavsc.Api.Client;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP client for <c>/api/circle</c> on the Yavsc Blogs server.
|
||||
///
|
||||
/// <para>Same conventions as <see cref="BlogApiClient"/>: all
|
||||
/// transport is delegated to <see cref="YavscApiClient"/>; this
|
||||
/// class only maps paths to DTOs.</para>
|
||||
///
|
||||
/// <para>The server now (since the BlogAcl fix on this branch)
|
||||
/// scopes every read and write to the caller's uid. There is no
|
||||
/// way for the client to read or modify another user's circles
|
||||
/// — the route will return 404 (not 403) when the circle exists
|
||||
/// but belongs to someone else, to avoid leaking its existence.</para>
|
||||
/// </summary>
|
||||
public sealed class CircleApiClient
|
||||
{
|
||||
private const string Path = "circle";
|
||||
|
||||
private readonly IYavscApiClient _api;
|
||||
|
||||
public CircleApiClient(IYavscApiClient api, string blogsBaseAddress)
|
||||
{
|
||||
_api = api ?? throw new ArgumentNullException(nameof(api));
|
||||
if (string.IsNullOrEmpty(blogsBaseAddress))
|
||||
throw new ArgumentException("Base address is required.", nameof(blogsBaseAddress));
|
||||
|
||||
if (api.Http.BaseAddress is null)
|
||||
api.Http.BaseAddress = new Uri(blogsBaseAddress);
|
||||
}
|
||||
|
||||
public Task<List<CircleDto>> GetMyCirclesAsync(CancellationToken ct = default)
|
||||
=> _api.CallAsync<List<CircleDto>>(HttpMethod.Get, Path, ct: ct);
|
||||
|
||||
public Task<CircleDto?> GetCircleAsync(long id, CancellationToken ct = default)
|
||||
=> _api.CallAsync<CircleDto?>(HttpMethod.Get, $"{Path}/{id}", ct: ct);
|
||||
|
||||
public Task<CircleDto?> CreateCircleAsync(CircleDto circle, CancellationToken ct = default)
|
||||
=> _api.CallAsync<CircleDto?>(HttpMethod.Post, Path, body: circle, ct: ct);
|
||||
|
||||
public Task UpdateCircleAsync(long id, CircleDto circle, CancellationToken ct = default)
|
||||
=> _api.CallAsync(HttpMethod.Put, $"{Path}/{id}", body: circle, ct: ct);
|
||||
|
||||
public Task DeleteCircleAsync(long id, CancellationToken ct = default)
|
||||
=> _api.CallAsync(HttpMethod.Delete, $"{Path}/{id}", ct: ct);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue