Files
ClawdDotNet/src/ClawdDotNet.Core/Deploymentcenter/BugtrackerClient.cs
T

90 lines
3.6 KiB
C#

using System.Text.Json;
namespace ClawdDotNet.Core.Deploymentcenter;
/// <summary>Was aus einem Bugtracker-Eintrag geworden ist.</summary>
/// <param name="ItemId">Nummer des Eintrags im Deploymentcenter.</param>
/// <param name="IsNew">False, wenn ein bestehender Eintrag hochgezählt wurde.</param>
/// <param name="OccurrenceCount">Wie oft dieses Vorkommnis bisher gezählt wurde.</param>
/// <param name="Url">Adresse der Übersicht, für einen Hinweis an den Benutzer.</param>
public sealed record BugtrackerReport(long ItemId, bool IsNew, int OccurrenceCount, string? Url);
/// <summary>Art eines Eintrags. Der Server kennt darüber hinaus noch <c>idea</c>.</summary>
public static class BugtrackerItemType
{
public const string Bug = "bug";
public const string Feature = "feature";
public const string Idea = "idea";
}
/// <summary>
/// Anbindung an <c>POST /api/bugtracker/v1/report</c> — der Weg, auf dem ClawdDotNet
/// selbst (oder ein Benutzer über die Oberfläche) einen Fehler oder Wunsch einträgt.
///
/// <para>Abgegrenzt vom <see cref="ErrorReporter"/>: Der meldet <em>ungefangene</em>
/// Ausnahmen automatisch; hier geht es um bewusst formulierte Einträge mit Titel und
/// Beschreibung. Serverseitig landen beide in derselben Tabelle — was richtig ist,
/// denn ein zweiter Speicher wäre nur ein zweiter Ort, an dem man suchen müsste.</para>
///
/// <para><b>Der Absender kommt aus dem Token</b> und lässt sich nicht frei wählen —
/// sonst könnte sich ein Agent als ein anderer ausgeben.</para>
/// </summary>
public sealed class BugtrackerClient(
DeploymentcenterApi api, string projectSlug, string environment, string build)
{
/// <param name="type">Siehe <see cref="BugtrackerItemType"/>.</param>
/// <param name="clientRef">
/// Freier Idempotenz-Schlüssel. Zweimal derselbe Wert erzeugt keinen zweiten
/// Eintrag — nützlich, wenn eine Meldung nach einem Verbindungsabbruch wiederholt
/// wird.
/// </param>
public async Task<BugtrackerReport> ReportAsync(
string type,
string title,
string? description = null,
string severity = "medium",
string? clientRef = null,
IReadOnlyDictionary<string, object?>? context = null,
CancellationToken ct = default)
{
var payload = new Dictionary<string, object?>
{
["project_slug"] = projectSlug,
["type"] = type,
["title"] = title,
["description"] = description,
["severity"] = severity,
["environment"] = environment,
["build_version"] = build
};
if (!string.IsNullOrWhiteSpace(clientRef))
payload["client_ref"] = clientRef;
if (context is { Count: > 0 })
payload["context"] = context;
var response = await api.PostAsync("/api/bugtracker/v1/report", payload, ct)
.ConfigureAwait(false);
return new BugtrackerReport(
ReadLong(response, "item_id"),
ReadBool(response, "is_new"),
(int)ReadLong(response, "occurrence_count"),
ReadString(response, "url"));
}
private static long ReadLong(JsonElement root, string name) =>
root.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.Number
? v.GetInt64()
: 0;
private static bool ReadBool(JsonElement root, string name) =>
root.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.True;
private static string? ReadString(JsonElement root, string name) =>
root.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.String
? v.GetString()
: null;
}