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.
55 lines
2.6 KiB
C#
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) { }
|
|
}
|