INoteStore reads the vault straight from Gitea (branch HEAD, a paged recursive tree listing, raw file reads); IFactSink appends a new raw/ file via the contents API, numbering around any path collision. Both verified against the live gitea.renner.uno API for response shape rather than the swagger doc. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
43 lines
2.1 KiB
C#
43 lines
2.1 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Text.Json;
|
|
|
|
namespace Wiki.Adapters.Gitea;
|
|
|
|
/// <summary>HTTP plumbing shared by <see cref="GiteaNoteStore"/> and <see cref="GiteaFactSink"/>:
|
|
/// client setup, JSON decoding and turning a failed response into a message worth reading.</summary>
|
|
internal static class GiteaHttp
|
|
{
|
|
public static readonly JsonSerializerOptions Json = new();
|
|
|
|
private const int ErrorBodyPreviewLength = 300;
|
|
|
|
/// <summary>Points the client at the configured Gitea and carries the token, unless the caller
|
|
/// (a test, typically) has already set these up on the HttpClient it handed in.</summary>
|
|
public static void Configure(HttpClient client, GiteaOptions options)
|
|
{
|
|
client.BaseAddress ??= new Uri(options.BaseUrl, UriKind.Absolute);
|
|
if (client.DefaultRequestHeaders.Authorization is null && !string.IsNullOrEmpty(options.Token))
|
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", options.Token);
|
|
}
|
|
|
|
public static async Task<T> GetJsonAsync<T>(HttpClient client, string url, CancellationToken ct)
|
|
{
|
|
using var response = await client.GetAsync(url, ct).ConfigureAwait(false);
|
|
await EnsureSuccessAsync(response, ct).ConfigureAwait(false);
|
|
await using var stream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false);
|
|
return await JsonSerializer.DeserializeAsync<T>(stream, Json, ct).ConfigureAwait(false)
|
|
?? throw new InvalidOperationException($"Gitea returned an empty body for {url}.");
|
|
}
|
|
|
|
public static async Task EnsureSuccessAsync(HttpResponseMessage response, CancellationToken ct)
|
|
{
|
|
if (response.IsSuccessStatusCode) return;
|
|
var body = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
|
if (body.Length > ErrorBodyPreviewLength) body = body[..ErrorBodyPreviewLength];
|
|
throw new HttpRequestException(
|
|
$"Gitea {(int)response.StatusCode} {response.ReasonPhrase} for {response.RequestMessage?.RequestUri}: {body}",
|
|
null,
|
|
response.StatusCode);
|
|
}
|
|
}
|