diff --git a/src/PostIt.Tests/BearerScopeTests.cs b/src/PostIt.Tests/BearerScopeTests.cs
index 1f47a176..68fa514e 100644
--- a/src/PostIt.Tests/BearerScopeTests.cs
+++ b/src/PostIt.Tests/BearerScopeTests.cs
@@ -8,9 +8,6 @@ using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using Yavsc.Blogspot;
-using Yavsc.Api.Client;
-using PostIt.Services;
using PostIt.Services;
using Xunit;
@@ -122,7 +119,7 @@ public class BearerScopeTests
// Resolve a BlogApiClient on top. We don't need real
// posts; we just need the outbound HTTP request to be
// the one we capture.
- var blog = new BlogApiClient(subClient, "http://localhost/");
+ var blog = new BlogApiClient(subClient);
await blog.GetPostsAsync(ct: TestContext.Current.CancellationToken);
diff --git a/src/PostIt.Tests/MainPageSaveTests.cs b/src/PostIt.Tests/MainPageSaveTests.cs
index 350a71f1..cea3e83f 100644
--- a/src/PostIt.Tests/MainPageSaveTests.cs
+++ b/src/PostIt.Tests/MainPageSaveTests.cs
@@ -3,7 +3,6 @@ using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.VisualTree;
using Yavsc.Blogspot;
-using Yavsc.Api.Client;
using PostIt.Services;
using PostIt.ViewModels;
using PostIt.Views;
@@ -41,7 +40,7 @@ public class MainPageSaveTests
// not a Control, so it needs a navigation host).
var recorder = new CallRecorder();
var api = new RecordingYavscApiClient(recorder);
- var blog = new BlogApiClient(api, "http://localhost/");
+ var blog = new BlogApiClient(api);
var viewModel = new MainPageViewModel(blog);
var page = new MainPage { DataContext = viewModel };
diff --git a/src/PostIt.Tests/PostItViewModelTests.cs b/src/PostIt.Tests/PostItViewModelTests.cs
index b964a18e..d8de8025 100644
--- a/src/PostIt.Tests/PostItViewModelTests.cs
+++ b/src/PostIt.Tests/PostItViewModelTests.cs
@@ -1,5 +1,4 @@
using Yavsc.Blogspot;
-using Yavsc.Api.Client;
using PostIt.Services;
using PostIt.ViewModels;
@@ -15,7 +14,7 @@ public class PostItViewModelTests
// default; tests construct one with a fake YavscApiClient that
// throws on any call (we never call the API in this test).
var fakeApi = new ThrowingYavscApiClient();
- var blog = new BlogApiClient(fakeApi, "http://localhost/");
+ var blog = new BlogApiClient(fakeApi);
var viewModel = new MainPageViewModel(blog);
viewModel.Posts.Add(new BlogPost { Id = 1, Title = "First post", Article = "Hello world", AuthorId = "alice" });
@@ -47,7 +46,7 @@ public class PostItViewModelTests
new() { Id = 2, Title = "World" }
};
var api = new StubYavscApiClient(expected);
- var blog = new BlogApiClient(api, "http://localhost/");
+ var blog = new BlogApiClient(api);
var posts = await blog.GetPostsAsync();
diff --git a/src/PostIt.Tests/YavscApiClientTests.cs b/src/PostIt.Tests/YavscApiClientTests.cs
index e54bc541..c020fec9 100644
--- a/src/PostIt.Tests/YavscApiClientTests.cs
+++ b/src/PostIt.Tests/YavscApiClientTests.cs
@@ -8,9 +8,6 @@ using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading;
-using Yavsc.Blogspot;
-using Yavsc.Api.Client;
-using PostIt.Services;
using System.Threading.Tasks;
using IdentityModel.OidcClient;
using IdentityModel.OidcClient.Browser;
diff --git a/src/PostIt/PostIt/App.axaml.cs b/src/PostIt/PostIt/App.axaml.cs
index 4a250ebe..b5740f2f 100644
--- a/src/PostIt/PostIt/App.axaml.cs
+++ b/src/PostIt/PostIt/App.axaml.cs
@@ -7,7 +7,6 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Styling;
using PostIt.Services;
-using Yavsc.Api.Client;
using PostIt.ViewModels;
using PostIt.Views;
@@ -56,7 +55,7 @@ public partial class App : Application
"PostIt", "tokens.json"));
var api = new YavscApiClient(settings, tokenStore);
- var client = new BlogApiClient(api, settings.BlogsApiUrl);
+ var client = new BlogApiClient(api);
var services = new ServiceCollection();
diff --git a/src/PostIt/PostIt/PostIt.csproj b/src/PostIt/PostIt/PostIt.csproj
index e4d51a88..d9cf96d3 100644
--- a/src/PostIt/PostIt/PostIt.csproj
+++ b/src/PostIt/PostIt/PostIt.csproj
@@ -25,7 +25,6 @@
-
diff --git a/src/Yavsc.Api.Client/BlogApiClient.cs b/src/PostIt/PostIt/Services/BlogApiClient.cs
similarity index 78%
rename from src/Yavsc.Api.Client/BlogApiClient.cs
rename to src/PostIt/PostIt/Services/BlogApiClient.cs
index 537124a7..f2061927 100644
--- a/src/Yavsc.Api.Client/BlogApiClient.cs
+++ b/src/PostIt/PostIt/Services/BlogApiClient.cs
@@ -5,16 +5,15 @@ using System.Threading;
using System.Threading.Tasks;
using Yavsc.Blogspot;
-namespace Yavsc.Api.Client;
+namespace PostIt.Services;
///
/// High-level client for the Blog subsystem of the Yavsc API
/// (deployed at https://blogs.pschneider.fr). All transport
/// concerns — base URL, JSON serialisation, Bearer auth, silent
/// refresh on 401, request body shaping — are delegated to
-/// , which lives in the consuming
-/// application (PostIt). This class is a thin DTO↔path mapper,
-/// nothing more.
+/// . This class is a thin DTO↔path
+/// mapper, nothing more.
///
/// URL convention. 's
/// BaseAddress already terminates with /api/v1/
@@ -35,20 +34,16 @@ public sealed class BlogApiClient
{
private const string DefaultPathPrefix = "blog";
- private readonly IYavscApiClient _api;
- private readonly Uri _baseAddress;
+ private readonly YavscApiClient _api;
private readonly string _pathPrefix;
- public BlogApiClient(IYavscApiClient api, string blogsBaseAddress, string pathPrefix = DefaultPathPrefix)
+ public BlogApiClient(YavscApiClient api, string pathPrefix = DefaultPathPrefix)
{
_api = api ?? throw new ArgumentNullException(nameof(api));
- if (string.IsNullOrEmpty(blogsBaseAddress))
- throw new ArgumentException("Base address is required.", nameof(blogsBaseAddress));
- // e.g. "https://blogs.pschneider.fr/api/v1/" — keep the
+ // ApiUrl is e.g. "https://blogs.pschneider.fr/api/v1/" — keep the
// trailing slash so relative paths ("posts") resolve correctly.
- _baseAddress = new Uri(blogsBaseAddress);
- api.Http.BaseAddress = _baseAddress;
+ api.Http.BaseAddress = new Uri(api.Settings.BlogsApiUrl);
_pathPrefix = pathPrefix?.TrimStart('/') ?? DefaultPathPrefix;
}
diff --git a/src/PostIt/PostIt/Services/YavscApiClient.cs b/src/PostIt/PostIt/Services/YavscApiClient.cs
index b611fe02..9ae1453b 100644
--- a/src/PostIt/PostIt/Services/YavscApiClient.cs
+++ b/src/PostIt/PostIt/Services/YavscApiClient.cs
@@ -9,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks;
using IdentityModel.OidcClient;
using PostIt.ViewModels;
-using Yavsc.Api.Client;
namespace PostIt.Services;
@@ -25,7 +24,7 @@ namespace PostIt.Services;
/// only refreshes once even if many
/// concurrent requests are in flight.
///
-public class YavscApiClient : IYavscApiClient, IAsyncDisposable
+public class YavscApiClient : IAsyncDisposable
{
// 60s of slack before the access_token's nominal expiry. Covers
// network latency + JWT validation on the server side.
diff --git a/src/PostIt/PostIt/ViewModels/MainPageViewModel.cs b/src/PostIt/PostIt/ViewModels/MainPageViewModel.cs
index a9864db0..e8024d7d 100644
--- a/src/PostIt/PostIt/ViewModels/MainPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/MainPageViewModel.cs
@@ -5,7 +5,6 @@ using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Yavsc.Blogspot;
-using Yavsc.Api.Client;
using PostIt.Services;
namespace PostIt.ViewModels;
diff --git a/src/Yavsc.Api.Client/BlogAclApiClient.cs b/src/Yavsc.Api.Client/BlogAclApiClient.cs
deleted file mode 100644
index 71263ca4..00000000
--- a/src/Yavsc.Api.Client/BlogAclApiClient.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-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;
-
-///
-/// HTTP client for /api/blogacl on the Yavsc Blogs server.
-///
-/// Each grants a single
-/// Circle access to a single BlogPost. The server
-/// scopes every endpoint to the caller's uid: only the author of
-/// the underlying blog post can list, create, modify, or delete
-/// its ACL entries.
-///
-public sealed class BlogAclApiClient
-{
- private const string Path = "blogacl";
-
- private readonly IYavscApiClient _api;
-
- public BlogAclApiClient(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> GetMyAclAsync(CancellationToken ct = default)
- => _api.CallAsync>(HttpMethod.Get, Path, ct: ct);
-
- public Task GetAclAsync(long circleId, CancellationToken ct = default)
- => _api.CallAsync(HttpMethod.Get, $"{Path}/{circleId}", ct: ct);
-
- public Task GrantAsync(CircleAuthorizationDto acl, CancellationToken ct = default)
- => _api.CallAsync(HttpMethod.Post, Path, body: acl, ct: ct);
-
- public Task UpdateAclAsync(long circleId, CircleAuthorizationDto acl, CancellationToken ct = default)
- => _api.CallAsync(HttpMethod.Put, $"{Path}/{circleId}", body: acl, ct: ct);
-
- public Task RevokeAsync(long circleId, CancellationToken ct = default)
- => _api.CallAsync(HttpMethod.Delete, $"{Path}/{circleId}", ct: ct);
-}
diff --git a/src/Yavsc.Api.Client/CircleApiClient.cs b/src/Yavsc.Api.Client/CircleApiClient.cs
deleted file mode 100644
index a8b04a40..00000000
--- a/src/Yavsc.Api.Client/CircleApiClient.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-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;
-
-///
-/// HTTP client for /api/circle on the Yavsc Blogs server.
-///
-/// Same conventions as : all
-/// transport is delegated to ; this
-/// class only maps paths to DTOs.
-///
-/// 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.
-///
-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> GetMyCirclesAsync(CancellationToken ct = default)
- => _api.CallAsync>(HttpMethod.Get, Path, ct: ct);
-
- public Task GetCircleAsync(long id, CancellationToken ct = default)
- => _api.CallAsync(HttpMethod.Get, $"{Path}/{id}", ct: ct);
-
- public Task CreateCircleAsync(CircleDto circle, CancellationToken ct = default)
- => _api.CallAsync(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);
-}
diff --git a/src/Yavsc.Api.Client/Dtos/CircleAuthorizationDto.cs b/src/Yavsc.Api.Client/Dtos/CircleAuthorizationDto.cs
deleted file mode 100644
index f5d1e50e..00000000
--- a/src/Yavsc.Api.Client/Dtos/CircleAuthorizationDto.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Yavsc.Api.Client.Dtos;
-
-///
-/// Wire format for GET /api/blogacl and friends.
-///
-/// The server-side
-/// Yavsc.Models.Access.CircleAuthorizationToBlogPost EF entity
-/// carries virtual navigation properties (Target,
-/// Allowed) that pull in the full BlogPost and Circle graphs.
-/// The client never needs them: when showing the ACL of a post, the
-/// UI already has the post, and the circles are looked up by id
-/// against the list returned by GET /api/circle.
-///
-public sealed class CircleAuthorizationDto
-{
- public long CircleId { get; set; }
- public long BlogPostId { get; set; }
- public bool Comment { get; set; }
-}
diff --git a/src/Yavsc.Api.Client/Dtos/CircleDto.cs b/src/Yavsc.Api.Client/Dtos/CircleDto.cs
deleted file mode 100644
index ed6980e2..00000000
--- a/src/Yavsc.Api.Client/Dtos/CircleDto.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Yavsc.Api.Client.Dtos;
-
-///
-/// Wire format for GET /api/circle and friends.
-///
-/// Field names match the JSON the server emits (camelCase via
-/// the default policy), so no
-/// [JsonPropertyName] attributes are required.
-///
-/// Mirrors the server-side Yavsc.Models.Relationship.Circle
-/// EF entity but stops short of the navigation properties
-/// (Owner, Members) which depend on
-/// ApplicationUser and other server-only types. The client
-/// only ever needs the id, name, and owner of a circle to drive
-/// the UI.
-///
-public sealed class CircleDto
-{
- public long Id { get; set; }
- public string Name { get; set; } = string.Empty;
- public string OwnerId { get; set; } = string.Empty;
- public bool Public { get; set; }
-}
diff --git a/src/Yavsc.Api.Client/IYavscApiClient.cs b/src/Yavsc.Api.Client/IYavscApiClient.cs
deleted file mode 100644
index 209ec07d..00000000
--- a/src/Yavsc.Api.Client/IYavscApiClient.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-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
deleted file mode 100644
index 5376856d..00000000
--- a/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
- 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
-
-
-
-
-
-
-
-