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

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
@@ -2,6 +2,7 @@ using Deal.Contracts.Integrations.Models;
using Deal.Modules.Cards.Application.Models;
using Deal.Modules.Kanban.Application.Abstractions;
using Deal.Modules.Kanban.Application.Models;
using Deal.SharedKernel.Errors;
namespace Deal.Modules.Kanban.Application.Services;
@@ -87,8 +88,9 @@ public sealed partial class CardsService
/// Перенос карточки в корзину
/// </summary>
/// <param name="cardId">Id карточки (<c>c_...</c>).</param>
/// <returns>Карточка после переноса (при no-op — как была) либо null — карточки нет (404).</returns>
public Task<CardDto?> TrashCardAsync(string cardId, CancellationToken ct)
/// <returns>Карточка после переноса (при no-op — как была).</returns>
/// <exception cref="NotFoundException">Карточка не найдена.</exception>
public Task<CardDto> TrashCardAsync(string cardId, CancellationToken ct)
{
return TrashCardAsync(cardId, teach: true, ct);
}
@@ -98,17 +100,15 @@ public sealed partial class CardsService
/// </summary>
/// <param name="cardId">Id карточки (<c>c_...</c>).</param>
/// <param name="teach">True — писать сигнал «спам» (действие пользователя); false — не писать.</param>
/// <returns>Карточка после переноса (при no-op — как была) либо null — карточки нет (404).</returns>
public async Task<CardDto?> TrashCardAsync(
/// <returns>Карточка после переноса (при no-op — как была).</returns>
/// <exception cref="NotFoundException">Карточка не найдена.</exception>
public async Task<CardDto> TrashCardAsync(
string cardId,
bool teach,
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 (card.Col == CardIds.Trash)
{
@@ -122,21 +122,20 @@ public sealed partial class CardsService
await _mlClient.PushAsync(text, MlLearningLabels.Spam, PushWeightUser, ct);
}
return await _store.GetCardAsync(cardId, ct);
return await _store.GetCardAsync(cardId, ct)
?? throw new NotFoundException(CardEntityName, cardId);
}
/// <summary>
/// Возврат карточки из архива/корзины на канбан.
/// </summary>
/// <param name="cardId">Id карточки (<c>c_...</c>).</param>
/// <returns>Колонка возврата (inbox/доска) либо null — карточки нет (404).</returns>
public async Task<string?> RestoreCardAsync(string cardId, CancellationToken ct)
/// <returns>Колонка возврата (inbox/доска).</returns>
/// <exception cref="NotFoundException">Карточка не найдена.</exception>
public async Task<string> RestoreCardAsync(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);
string back = await ResolveReturnColAsync(card.PrevCol, ct);
string text = LearningText(card);