Convert PocketBase dates to Berlin time; document code conventions
This commit is contained in:
1 parent
4b93434679
commit
aef05fb090
11 files changed
+406
-129
No files matched your search
@@ -5,19 +5,89 @@ using Elternbeirat.Contracts;
|
||||
namespace Elternbeirat.PocketBase;
|
||||
|
||||
/// <summary>
|
||||
/// Reads published content from a PocketBase instance over its REST API.
|
||||
/// <para>
|
||||
/// One method per collection (pages, posts, events, faqs). Each returns only
|
||||
/// records with <c>public = true</c> and lets PocketBase do the filtering and
|
||||
/// sorting via query parameters. The <see cref="HttpClient"/> is expected to have
|
||||
/// its <see cref="HttpClient.BaseAddress"/> set to the PocketBase base URL, so it
|
||||
/// is registered as a typed client via <c>AddHttpClient</c>.
|
||||
/// </para>
|
||||
/// Reads published content from a PocketBase instance over its REST API.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// There is one method per collection (pages, posts, events, faqs). Each returns
|
||||
/// only records with <c>public = true</c> and leaves filtering and sorting to
|
||||
/// PocketBase via query parameters.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Register the class as a typed client via <c>AddHttpClient</c>, with
|
||||
/// <see cref="HttpClient.BaseAddress"/> set to the PocketBase base URL.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="http">
|
||||
/// The HTTP client; its <see cref="HttpClient.BaseAddress"/> must point to PocketBase.
|
||||
/// </param>
|
||||
public sealed class PocketBaseClient(HttpClient http)
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializer options that map PocketBase UTC dates to Berlin wall-clock time.
|
||||
/// </summary>
|
||||
private static readonly JsonSerializerOptions JsonOptions = CreateJsonOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all public pages, ordered by <see cref="Page.Order"/>.
|
||||
/// </summary>
|
||||
/// <param name="ct">
|
||||
/// A token to cancel the request.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The public pages, or an empty list if there are none.
|
||||
/// </returns>
|
||||
/// <exception cref="HttpRequestException">
|
||||
/// PocketBase could not be reached or returned a non-success status code.
|
||||
/// </exception>
|
||||
/// <exception cref="JsonException">
|
||||
/// The response could not be deserialized.
|
||||
/// </exception>
|
||||
/// <exception cref="OperationCanceledException">
|
||||
/// <paramref name="ct"/> was canceled or the request timed out.
|
||||
/// </exception>
|
||||
public Task<IReadOnlyList<Page>> GetPagesAsync(CancellationToken ct = default)
|
||||
=> GetRecordsAsync<Page>("pages", "order", ct);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all public posts, ordered by <see cref="Post.Date"/>, newest first.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="GetPagesAsync" path="/param"/>
|
||||
/// <returns>
|
||||
/// The public posts, or an empty list if there are none.
|
||||
/// </returns>
|
||||
/// <inheritdoc cref="GetPagesAsync" path="/exception"/>
|
||||
public Task<IReadOnlyList<Post>> GetPostsAsync(CancellationToken ct = default)
|
||||
=> GetRecordsAsync<Post>("posts", "-date", ct);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all public events, ordered by <see cref="Event.Start"/>, earliest first.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="GetPagesAsync" path="/param"/>
|
||||
/// <returns>
|
||||
/// The public events, or an empty list if there are none.
|
||||
/// </returns>
|
||||
/// <inheritdoc cref="GetPagesAsync" path="/exception"/>
|
||||
public Task<IReadOnlyList<Event>> GetEventsAsync(CancellationToken ct = default)
|
||||
=> GetRecordsAsync<Event>("events", "start", ct);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all public FAQ entries, ordered by <see cref="Faq.Topic"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="GetPagesAsync" path="/param"/>
|
||||
/// <returns>
|
||||
/// The public FAQ entries, or an empty list if there are none.
|
||||
/// </returns>
|
||||
/// <inheritdoc cref="GetPagesAsync" path="/exception"/>
|
||||
public Task<IReadOnlyList<Faq>> GetFaqsAsync(CancellationToken ct = default)
|
||||
=> GetRecordsAsync<Faq>("faqs", "topic", ct);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the serializer options with both wall-clock date converters registered.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The configured options.
|
||||
/// </returns>
|
||||
private static JsonSerializerOptions CreateJsonOptions()
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
@@ -29,23 +99,28 @@ public sealed class PocketBaseClient(HttpClient http)
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>Gets all public pages, ordered for navigation.</summary>
|
||||
public Task<IReadOnlyList<Page>> GetPagesAsync(CancellationToken ct = default)
|
||||
=> GetRecordsAsync<Page>("pages", "order", ct);
|
||||
|
||||
/// <summary>Gets all public posts, newest first.</summary>
|
||||
public Task<IReadOnlyList<Post>> GetPostsAsync(CancellationToken ct = default)
|
||||
=> GetRecordsAsync<Post>("posts", "-date", ct);
|
||||
|
||||
/// <summary>Gets all public events, earliest start first.</summary>
|
||||
public Task<IReadOnlyList<Event>> GetEventsAsync(CancellationToken ct = default)
|
||||
=> GetRecordsAsync<Event>("events", "start", ct);
|
||||
|
||||
/// <summary>Gets all public FAQ entries.</summary>
|
||||
public Task<IReadOnlyList<Faq>> GetFaqsAsync(CancellationToken ct = default)
|
||||
=> GetRecordsAsync<Faq>("faqs", "topic", ct);
|
||||
|
||||
private async Task<IReadOnlyList<T>> GetRecordsAsync<T>(string collection, string sort, CancellationToken ct)
|
||||
/// <summary>
|
||||
/// Gets all public records of a collection in a single request.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The record type to deserialize into.
|
||||
/// </typeparam>
|
||||
/// <param name="collection">
|
||||
/// The PocketBase collection name, e.g. <c>"posts"</c>.
|
||||
/// </param>
|
||||
/// <param name="sort">
|
||||
/// The PocketBase sort expression; a leading <c>-</c> sorts descending.
|
||||
/// </param>
|
||||
/// <param name="ct">
|
||||
/// A token to cancel the request.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The public records, or an empty list if there are none.
|
||||
/// </returns>
|
||||
private async Task<IReadOnlyList<T>> GetRecordsAsync<T>(
|
||||
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.
|
||||
|
||||
Reference in new issue
Block a user