using System.Net.Http.Json;
using System.Text.Json;
using Elternbeirat.Contracts;
namespace Elternbeirat.PocketBase;
///
/// Reads published content from a PocketBase instance over its REST API.
///
///
///
/// There is one method per collection (pages, posts, events, faqs). Each returns
/// only records with public = true and leaves filtering and sorting to
/// PocketBase via query parameters.
///
///
/// Register the class as a typed client via AddHttpClient, with
/// set to the PocketBase base URL.
///
///
///
/// The HTTP client; its must point to PocketBase.
///
public sealed class PocketBaseClient(HttpClient http)
{
///
/// Serializer options that map PocketBase UTC dates to Berlin wall-clock time.
///
private static readonly JsonSerializerOptions JsonOptions = CreateJsonOptions();
///
/// Gets all public pages, ordered by .
///
///
/// A token to cancel the request.
///
///
/// The public pages, or an empty list if there are none.
///
///
/// PocketBase could not be reached or returned a non-success status code.
///
///
/// The response could not be deserialized.
///
///
/// was canceled or the request timed out.
///
public Task> GetPagesAsync(CancellationToken ct = default)
=> GetRecordsAsync("pages", "order", ct);
///
/// Gets all public posts, ordered by , newest first.
///
///
///
/// The public posts, or an empty list if there are none.
///
///
public Task> GetPostsAsync(CancellationToken ct = default)
=> GetRecordsAsync("posts", "-date", ct);
///
/// Gets all public events, ordered by , earliest first.
///
///
///
/// The public events, or an empty list if there are none.
///
///
public Task> GetEventsAsync(CancellationToken ct = default)
=> GetRecordsAsync("events", "start", ct);
///
/// Gets all public FAQ entries, ordered by .
///
///
///
/// The public FAQ entries, or an empty list if there are none.
///
///
public Task> GetFaqsAsync(CancellationToken ct = default)
=> GetRecordsAsync("faqs", "topic", ct);
///
/// Creates the serializer options with the date converters registered: the
/// wall-clock ones for points in time and the calendar-day one for plain dates.
///
///
/// The configured options.
///
private static JsonSerializerOptions CreateJsonOptions()
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
options.Converters.Add(new LocalDateTimeConverter());
options.Converters.Add(new NullableLocalDateTimeConverter());
options.Converters.Add(new DateOnlyConverter());
return options;
}
///
/// Gets all public records of a collection in a single request.
///
///
/// The record type to deserialize into.
///
///
/// The PocketBase collection name, e.g. "posts".
///
///
/// The PocketBase sort expression; a leading - sorts descending.
///
///
/// A token to cancel the request.
///
///
/// The public records, or an empty list if there are none.
///
private async Task> GetRecordsAsync(
string collection,
string sort,
CancellationToken ct)
{
// filter=public=true keeps drafts out; perPage is large enough to fetch
// every record in a single page given the small content volume.
var url = $"/api/collections/{collection}/records"
+ $"?perPage=500&filter={Uri.EscapeDataString("public=true")}"
+ $"&sort={Uri.EscapeDataString(sort)}";
var result = await http.GetFromJsonAsync>(url, JsonOptions, ct);
return result?.Items ?? [];
}
}