Перевести «не найдено» карточек и колонок на исключения

CardsService (карточка/файл) и ContainersService бросают NotFoundException вместо возврата null; эндпоинты больше не проверяют null — 404 отдаёт общий обработчик. Тесты обновлены под новое поведение.
This commit is contained in:
2026-09-13 14:32:24 +03:00
parent 7c43c40282
commit 685c5fdf46
11 changed files with 121 additions and 152 deletions
@@ -3,6 +3,7 @@ using Deal.Modules.Cards.Application.Models;
using Deal.Modules.Cards.Application.Sources;
using Deal.Modules.Kanban.Application.Abstractions;
using Deal.Modules.Kanban.Application.Models;
using Deal.SharedKernel.Errors;
namespace Deal.Modules.Kanban.Application.Services;
@@ -101,14 +102,12 @@ public sealed partial class CardsService
/// «Взять в работу»
/// </summary>
/// <param name="cardId">Id карточки (<c>c_...</c>).</param>
/// <returns>Карточка в стадии planned; null — карточки нет (404).</returns>
public async Task<CardDto?> TakeCardAsync(string cardId, CancellationToken ct)
/// <returns>Карточка в стадии planned.</returns>
/// <exception cref="NotFoundException">Карточка не найдена.</exception>
public async Task<CardDto> TakeCardAsync(string cardId, CancellationToken ct)
{
CardDto? card = await _store.GetCardAsync(cardId, ct);
if (card is null)
{
return null;
}
CardDto card = await _store.GetCardAsync(cardId, ct)
?? throw new NotFoundException(CardEntityName, cardId);
if (CardsDefaultContainers.Contains(card.Col))
{
@@ -120,14 +119,14 @@ public sealed partial class CardsService
if (!await _store.MoveCardStageAsync(cardId, PlannedStage, entry, nowMs, ct))
{
// Карточка исчезла между чтением и переносом (гонка с удалением).
return null;
throw new NotFoundException(CardEntityName, cardId);
}
await _store.AddCommentAsync(
PrefixId.New(KanbanIdPrefixes.Comment), cardId, CommentAuthor, TakenCommentText, ct);
return await _store.GetCardAsync(cardId, ct)
?? throw new InvalidOperationException("Карточка не прочиталась после take: " + cardId);
?? throw new NotFoundException(CardEntityName, cardId);
}
/// <summary>
@@ -135,8 +134,9 @@ public sealed partial class CardsService
/// </summary>
/// <param name="cardId">Id карточки (<c>c_...</c>).</param>
/// <param name="body">Тело PATCH: ключ → JSON-значение (наличие ключа = поле меняется).</param>
/// <returns>Обновлённая карточка или null — карточки нет (404).</returns>
public async Task<CardDto?> PatchCardAsync(
/// <returns>Обновлённая карточка.</returns>
/// <exception cref="NotFoundException">Карточка не найдена.</exception>
public async Task<CardDto> PatchCardAsync(
string cardId,
IReadOnlyDictionary<string, JsonElement> body,
CancellationToken ct)
@@ -144,7 +144,13 @@ public sealed partial class CardsService
ArgumentNullException.ThrowIfNull(body);
bool updated = await _store.PatchCardAsync(cardId, ResolvePatch(body), ct);
return updated ? await _store.GetCardAsync(cardId, ct) : null;
if (!updated)
{
throw new NotFoundException(CardEntityName, cardId);
}
return await _store.GetCardAsync(cardId, ct)
?? throw new NotFoundException(CardEntityName, cardId);
}
/// <summary>