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) { /// /// Shared serializer options for every request; see . /// 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. Date handling lives on the contracts /// themselves via [JsonConverter] on each date field (e.g. /// , ), so /// nothing date-related is registered here. /// /// /// The configured options. /// private static JsonSerializerOptions CreateJsonOptions() => new() { PropertyNameCaseInsensitive = true }; /// /// 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 ?? []; } }