Files
ClaudeDo/src/ClaudeDo.Worker/Tickets/TicketDtos.cs
T
mika kuns 30d0611b8e fix(worker): fix Bandel ticket sync and reactivate the integration
The status PATCH needs statusName alongside statusId or the API 400s,
so every write-back had been silently failing. The board's flat
assigneeName is null on every live ticket; the real assignee list is
ticketAssignees[]. Import now also picks up In Bearbeitung tickets,
not just Offen ones.

Both issues are live-verified against the real API, so the feature
flag flips back on.
2026-09-02 09:59:06 +02:00

55 lines
2.6 KiB
C#

using System.Text.Json.Serialization;
namespace ClaudeDo.Worker.Tickets;
/// Antwort-Hülle jeder Bandel.APIs-Route.
public sealed record BandelEnvelope<T>(
[property: JsonPropertyName("success")] bool Success,
[property: JsonPropertyName("data")] T? Data,
[property: JsonPropertyName("message")] string? Message,
[property: JsonPropertyName("errorCode")] string? ErrorCode);
/// GET /api/ticketsystem/pat/me
public sealed record TicketIdentity(
[property: JsonPropertyName("userName")] string UserName,
[property: JsonPropertyName("isPat")] bool IsPat,
[property: JsonPropertyName("scopes")] IReadOnlyList<string> Scopes);
/// Ein Board-Item (Ausschnitt aus GetTicketSummaryDto — nur was der Import braucht).
/// ⚠️ <see cref="AssigneeId"/>/<see cref="AssigneeName"/> sind live durchgehend null (geprüft über
/// alle 63 Tickets der Abteilung Entwicklung, 2026-09-01). Die echten Zuweisungen stehen in
/// <see cref="TicketAssignees"/>; nur dessen <c>assigneeName</c> hat dasselbe Format wie
/// <see cref="TicketIdentity.UserName"/> (<c>mika.kuns</c>). <c>personInChargeName</c> wäre die
/// falsche Quelle — dort steht mal der Login, mal der Anzeigename ("Mika Kuns").
public sealed record TicketBoardItem(
[property: JsonPropertyName("id")] int Id,
[property: JsonPropertyName("title")] string Title,
[property: JsonPropertyName("description")] string? Description,
[property: JsonPropertyName("statusId")] int StatusId,
[property: JsonPropertyName("statusName")] string? StatusName,
[property: JsonPropertyName("assigneeID")] int? AssigneeId,
[property: JsonPropertyName("assigneeName")] string? AssigneeName,
[property: JsonPropertyName("ticketAssignees")] IReadOnlyList<TicketAssignee>? TicketAssignees = null);
public sealed record TicketAssignee(
[property: JsonPropertyName("assigneeId")] int AssigneeId,
[property: JsonPropertyName("assigneeName")] string? AssigneeName);
/// GET /api/Navigation/sidebar — Abteilung mit ihren Projekten.
public sealed record SidebarDepartment(
[property: JsonPropertyName("departmentId")] int DepartmentId,
[property: JsonPropertyName("departmentName")] string DepartmentName,
[property: JsonPropertyName("projects")] IReadOnlyList<SidebarProject> Projects);
public sealed record SidebarProject(
[property: JsonPropertyName("id")] int Id,
[property: JsonPropertyName("title")] string Title);
/// Flachgeklopftes Projekt für die UI-Auswahl.
public sealed record TicketProject(int Id, string Title, string DepartmentName);
public sealed class TicketApiException : Exception
{
public TicketApiException(string message) : base(message) { }
}