diff --git a/backend/src/Tnsu.ApprovalRoute.Api/Common/ApiResponse.cs b/backend/src/Tnsu.ApprovalRoute.Api/Common/ApiResponse.cs index 36f539e..48561fe 100644 --- a/backend/src/Tnsu.ApprovalRoute.Api/Common/ApiResponse.cs +++ b/backend/src/Tnsu.ApprovalRoute.Api/Common/ApiResponse.cs @@ -1,5 +1,5 @@ namespace Tnsu.ApprovalRoute.Api.Common; -public sealed record ApiResponse(T Data, IReadOnlyList Warnings); +public sealed record ApiResponse(T Data); public sealed record ApiError(string Message); diff --git a/backend/src/Tnsu.ApprovalRoute.Api/Common/ResultExtensions.cs b/backend/src/Tnsu.ApprovalRoute.Api/Common/ResultExtensions.cs index 26c7bef..12ac8bb 100644 --- a/backend/src/Tnsu.ApprovalRoute.Api/Common/ResultExtensions.cs +++ b/backend/src/Tnsu.ApprovalRoute.Api/Common/ResultExtensions.cs @@ -11,8 +11,7 @@ public static class ResultExtensions if (result.IsFailed) return result.Errors[0].ToActionResult(); - var warnings = result.Reasons.OfType().Select(w => w.Message).ToList(); - return new OkObjectResult(new ApiResponse(result.Value, warnings)); + return new OkObjectResult(new ApiResponse(result.Value)); } public static IActionResult ToActionResult(this Result result) diff --git a/backend/src/Tnsu.ApprovalRoute.Application/Common/Errors/ApiErrors.cs b/backend/src/Tnsu.ApprovalRoute.Application/Common/Errors/ApiErrors.cs index e9518c8..4285651 100644 --- a/backend/src/Tnsu.ApprovalRoute.Application/Common/Errors/ApiErrors.cs +++ b/backend/src/Tnsu.ApprovalRoute.Application/Common/Errors/ApiErrors.cs @@ -8,5 +8,3 @@ public sealed class NotFoundError(string entity, object id) public sealed class ValidationError(string message) : Error(message); public sealed class ConflictError(string message) : Error(message); - -public sealed class Warning(string message) : Success(message); diff --git a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepDto.cs b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepDto.cs index 6966de9..d24d5f0 100644 --- a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepDto.cs +++ b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepDto.cs @@ -11,7 +11,8 @@ public sealed record TemplateRouteStepDto( HeadType HeadType, RouteStepStatus Status, int RouteId, - bool AllowReassignToOtherApprovers); + bool AllowReassignToOtherApprovers, + string? Warning = null); public sealed record CreateTemplateRouteStepRequest( int OrderNo, diff --git a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepService.cs b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepService.cs index 244c35b..9e1f094 100644 --- a/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepService.cs +++ b/backend/src/Tnsu.ApprovalRoute.Application/TemplateRouteSteps/TemplateRouteStepService.cs @@ -55,8 +55,7 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db) db.TemplateRouteSteps.Add(entity); await db.SaveChangesAsync(cancellationToken); - var result = Result.Ok(ToDto(entity)); - return await AppendMissingProjectRoleWarningAsync(route, result, cancellationToken); + return Result.Ok(await ToDtoWithWarningAsync(entity, route, cancellationToken)); } public async Task> UpdateAsync( @@ -83,8 +82,7 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db) entity.AllowReassignToOtherApprovers = r.AllowReassignToOtherApprovers; await db.SaveChangesAsync(cancellationToken); - var result = Result.Ok(ToDto(entity)); - return await AppendMissingProjectRoleWarningAsync(route, result, cancellationToken); + return Result.Ok(await ToDtoWithWarningAsync(entity, route, cancellationToken)); } public async Task DeleteAsync(int routeId, int id, CancellationToken cancellationToken) @@ -99,18 +97,20 @@ public sealed class TemplateRouteStepService(ApprovalRouteDbContext db) return Result.Ok(); } - private async Task> AppendMissingProjectRoleWarningAsync( - TemplateRoute route, Result result, CancellationToken cancellationToken) + // Для маршрута «по проекту» без единого шага с ролью в проекте DTO несёт предупреждение в поле Warning. + private async Task ToDtoWithWarningAsync( + TemplateRouteStep entity, TemplateRoute route, CancellationToken cancellationToken) { - if (!route.ByProject) - return result; + string? warning = null; + if (route.ByProject) + { + var hasProjectRoleStep = await db.TemplateRouteSteps + .AnyAsync(x => x.RouteId == route.Id && x.ProjectRole != ProjectRole.None, cancellationToken); + if (!hasProjectRoleStep) + warning = "Должен быть хотя бы один шаг с ролью в проекте."; + } - var hasProjectRoleStep = await db.TemplateRouteSteps - .AnyAsync(x => x.RouteId == route.Id && x.ProjectRole != ProjectRole.None, cancellationToken); - - return hasProjectRoleStep - ? result - : result.WithReason(new Warning("Должен быть хотя бы один шаг с ролью в проекте.")); + return ToDto(entity) with { Warning = warning }; } private static TemplateRouteStepDto ToDto(TemplateRouteStep e) => new( diff --git a/backend/tests/Tnsu.ApprovalRoute.IntegrationTests/ApplicationServices/TemplateRouteStepServiceTests.cs b/backend/tests/Tnsu.ApprovalRoute.IntegrationTests/ApplicationServices/TemplateRouteStepServiceTests.cs index 16c56ee..a090faa 100644 --- a/backend/tests/Tnsu.ApprovalRoute.IntegrationTests/ApplicationServices/TemplateRouteStepServiceTests.cs +++ b/backend/tests/Tnsu.ApprovalRoute.IntegrationTests/ApplicationServices/TemplateRouteStepServiceTests.cs @@ -99,7 +99,8 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture r.Message.Contains("ролью в проекте")); + created.Value.Warning.ShouldNotBeNull(); + created.Value.Warning.ShouldContain("ролью в проекте"); } [Fact(DisplayName = "Создание шага с ролью в проекте на маршруте «по проекту» не возвращает предупреждение")] @@ -116,7 +117,7 @@ public sealed class TemplateRouteStepServiceTests : IClassFixture