diff --git a/AsbCloudApp/Data/WellOperationDto.cs b/AsbCloudApp/Data/WellOperationDto.cs
index 31b50243..f2a2d65f 100644
--- a/AsbCloudApp/Data/WellOperationDto.cs
+++ b/AsbCloudApp/Data/WellOperationDto.cs
@@ -1,4 +1,4 @@
-using AsbCloudApp.ValidationAttributes;
+using AsbCloudApp.Validation;
using System;
using System.ComponentModel.DataAnnotations;
diff --git a/AsbCloudApp/Exceptions/ArgumentInvalidException.cs b/AsbCloudApp/Exceptions/ArgumentInvalidException.cs
index 987401bc..af239d00 100644
--- a/AsbCloudApp/Exceptions/ArgumentInvalidException.cs
+++ b/AsbCloudApp/Exceptions/ArgumentInvalidException.cs
@@ -22,25 +22,5 @@ namespace AsbCloudApp.Exceptions
{
ParamName = paramName;
}
-
- ///
- /// преобразование в объект валидации
- ///
- ///
- public object ToValidationErrorObject()
- => MakeValidationError(ParamName, Message);
-
- ///
- /// фабрика объекта валидации
- ///
- ///
- ///
- ///
- public static object MakeValidationError(string paramName, params string[] errors)
- => new
- {
- name = paramName,
- errors,
- };
}
}
diff --git a/AsbCloudApp/Requests/ReportParametersRequest.cs b/AsbCloudApp/Requests/ReportParametersRequest.cs
index 465662e1..7a9061a5 100644
--- a/AsbCloudApp/Requests/ReportParametersRequest.cs
+++ b/AsbCloudApp/Requests/ReportParametersRequest.cs
@@ -1,4 +1,4 @@
-using AsbCloudApp.ValidationAttributes;
+using AsbCloudApp.Validation;
using System;
namespace AsbCloudApp.Requests;
diff --git a/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs b/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs
index 11aafa3c..9080b596 100644
--- a/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs
+++ b/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs
@@ -1,23 +1,27 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
+using System.Linq;
namespace AsbCloudApp.Requests
{
///
/// класс с фильтрами для запроса
///
- public class SubsystemOperationTimeRequest: RequestBase
+ public class SubsystemOperationTimeRequest: RequestBase, IValidatableObject
{
+ private static readonly DateTime validationMinDate = new DateTime(2020,01,01,0,0,0,DateTimeKind.Utc);
+
///
/// идентификатор скважины
///
[Required]
public int IdWell { get; set; }
+
///
/// идентификатор подсистемы
///
- public IEnumerable? IdsSubsystems { get; set; }
+ public IEnumerable IdsSubsystems { get; set; } = Enumerable.Empty();
///
/// Больше или равно дате
@@ -58,5 +62,32 @@ namespace AsbCloudApp.Requests
/// Режим выборки элементов
///
public int SelectMode { get; set; } = SelectModeOuter;
+
+ ///
+ public IEnumerable Validate(ValidationContext validationContext)
+ {
+ if (GtDate.HasValue && GtDate < validationMinDate)
+ yield return new ValidationResult(
+ $"Должно быть больше {validationMinDate:O})",
+ new[] { nameof(GtDate) });
+
+ if (LtDate.HasValue && GtDate.HasValue)
+ {
+ if (LtDate < GtDate)
+ yield return new ValidationResult(
+ $"{nameof(LtDate)} должно быть больше {nameof(GtDate)}. ({LtDate:O} < {GtDate:O})",
+ new[] { nameof(LtDate), nameof(GtDate) });
+ }
+
+ if (LtDepth.HasValue && GtDepth.HasValue)
+ {
+ if (LtDepth < GtDepth)
+ yield return new ValidationResult(
+ $"{nameof(LtDepth)} должно быть больше {nameof(GtDepth)}. ({LtDepth:O} < {GtDepth:O})",
+ new[] { nameof(LtDepth), nameof(GtDepth) });
+ }
+
+ yield break;
+ }
}
}
diff --git a/AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs b/AsbCloudApp/Validation/DateValidationAttribute.cs
similarity index 98%
rename from AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs
rename to AsbCloudApp/Validation/DateValidationAttribute.cs
index 3ad6fce0..925739a4 100644
--- a/AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs
+++ b/AsbCloudApp/Validation/DateValidationAttribute.cs
@@ -1,7 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
-namespace AsbCloudApp.ValidationAttributes
+namespace AsbCloudApp.Validation
{
///
/// Атрибут валидации даты-времени
diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs
index 0bb5acd6..1b26b03f 100644
--- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs
+++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs
@@ -345,7 +345,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems
.Where(o => o.IdTelemetry == well.IdTelemetry)
.AsNoTracking();
- if (request.IdsSubsystems?.Any() == true)
+ if (request.IdsSubsystems.Any())
query = query.Where(o => request.IdsSubsystems.Contains(o.IdSubsystem));
// # Dates range condition
diff --git a/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs b/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs
index 3e4990a2..35c446e0 100644
--- a/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs
+++ b/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs
@@ -1,4 +1,4 @@
-using AsbCloudApp.ValidationAttributes;
+using AsbCloudApp.Validation;
using System;
using Xunit;
diff --git a/AsbCloudWebApi/Controllers/DrillingProgramController.cs b/AsbCloudWebApi/Controllers/DrillingProgramController.cs
index fc4ad8ea..435f0b50 100644
--- a/AsbCloudWebApi/Controllers/DrillingProgramController.cs
+++ b/AsbCloudWebApi/Controllers/DrillingProgramController.cs
@@ -119,10 +119,10 @@ namespace AsbCloudWebApi.Controllers
return Forbid();
if (files.Count > 1)
- return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(files), "only 1 file can be uploaded"));
+ throw new ArgumentInvalidException("only 1 file can be uploaded", nameof(files));
if (files.Count == 0)
- return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(files), "at list 1 file should be uploaded"));
+ throw new ArgumentInvalidException("at list 1 file should be uploaded", nameof(files));
var fileName = files[0].FileName;
diff --git a/AsbCloudWebApi/Controllers/NotificationController.cs b/AsbCloudWebApi/Controllers/NotificationController.cs
index 2d37693d..3332e012 100644
--- a/AsbCloudWebApi/Controllers/NotificationController.cs
+++ b/AsbCloudWebApi/Controllers/NotificationController.cs
@@ -83,18 +83,14 @@ public class NotificationController : ControllerBase
///
[HttpGet("{idNotification}")]
[ProducesResponseType(typeof(NotificationDto), (int)System.Net.HttpStatusCode.OK)]
- public async Task GetAsync([Required] int idNotification,
+ [ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
+ public async Task GetAsync([Required] int idNotification,
CancellationToken cancellationToken)
{
- var notification = await notificationRepository.GetOrDefaultAsync(idNotification, cancellationToken);
+ var notification = await notificationRepository.GetOrDefaultAsync(idNotification, cancellationToken)
+ ?? throw new ArgumentInvalidException("Уведомление не найдено", nameof(idNotification));
- if (notification is null)
- {
- return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(idNotification),
- "Уведомление не найдено"));
- }
-
- return Ok(notification);
+ return Ok(notification);
}
///
diff --git a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
index 0a16bcfd..2f644988 100644
--- a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
+++ b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
@@ -128,7 +128,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
public async Task ExportAsync(int? idWell, int? idCluster, CancellationToken token)
{
if (idCluster is null && idWell is null)
- return this.MakeBadRequest(nameof(idWell), $"One of {nameof(idWell)} or {nameof(idCluster)} mast be set.");
+ return this.ValidationBadRequest(nameof(idWell), $"One of {nameof(idWell)} or {nameof(idCluster)} mast be set.");
int? idCompany = User.GetCompanyId();
diff --git a/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs b/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs
index 1c7279b7..f7bc7afd 100644
--- a/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs
+++ b/AsbCloudWebApi/Controllers/Subsystems/SubsystemOperationTimeController.cs
@@ -1,5 +1,6 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.Subsystems;
+using AsbCloudApp.Exceptions;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudApp.Services.Subsystems;
@@ -50,12 +51,12 @@ namespace AsbCloudWebApi.Controllers.Subsystems
///
[HttpGet("stat")]
[ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task GetStatAsync([FromQuery] SubsystemOperationTimeRequest request, CancellationToken token)
{
if (!await UserHasAccesToWellAsync(request.IdWell, token))
return Forbid();
- if (!await IsValidRequest(request, token))
- return BadRequest("Запрашиваемый диапазон должен заканчиваться за 2 часа до текущего времени(после приведения к UTC).");
+ await CustomValidate(request, token);
var subsystemResult = await subsystemOperationTimeService.GetStatAsync(request, token);
return Ok(subsystemResult);
}
@@ -125,15 +126,14 @@ namespace AsbCloudWebApi.Controllers.Subsystems
///
[HttpGet("operationTime")]
[ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)]
-
+ [ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task GetOperationTimeAsync(
[FromQuery] SubsystemOperationTimeRequest request,
CancellationToken token)
{
if (!await UserHasAccesToWellAsync(request.IdWell, token))
return Forbid();
- if (!await IsValidRequest(request, token))
- return BadRequest("Запрашиваемый диапазон должен заканчиваться за 2 часа до текущего времени(после приведения к UTC).");
+ await CustomValidate(request, token);
var result = await subsystemOperationTimeService.GetOperationTimeAsync(request, token);
return Ok(result);
@@ -148,14 +148,14 @@ namespace AsbCloudWebApi.Controllers.Subsystems
[HttpDelete]
[Permission]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public async Task DeleteAsync(
[FromQuery] SubsystemOperationTimeRequest request,
CancellationToken token)
{
if (!await UserHasAccesToWellAsync(request.IdWell, token))
return Forbid();
- if (!await IsValidRequest(request, token))
- return BadRequest("Запрашиваемый диапазон должен заканчиваться за 2 часа до текущего времени(после приведения к UTC).");
+ await CustomValidate(request, token);
var result = await subsystemOperationTimeService.DeleteAsync(request, token);
return Ok(result);
}
@@ -180,20 +180,24 @@ namespace AsbCloudWebApi.Controllers.Subsystems
return true;
return false;
}
- protected async Task IsValidRequest(SubsystemOperationTimeRequest request, CancellationToken token)
+
+ ///
+ /// Валидирует запрос и бросает исключение ArgumentInvalidException
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task CustomValidate(SubsystemOperationTimeRequest request, CancellationToken token)
{
var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
if (well is not null && request.LtDate.HasValue)
{
- var ltDate = (DateTimeOffset)request.LtDate;
- var utcDateRequest = ltDate.ToRemoteDateTime(well.Timezone.Hours);
-
+ var ltDate = request.LtDate.Value;
+ var utcDateRequest = ltDate.ToUtcDateTimeOffset(well.Timezone.Hours);
if (utcDateRequest.AddHours(2) > DateTime.UtcNow)
- {
- return false;
- }
+ throw new ArgumentInvalidException("Запрашиваемый диапазон должен заканчиваться за 2 часа до текущего времени", nameof(request.LtDate));
}
- return true;
}
}
}
diff --git a/AsbCloudWebApi/Controllers/UserSettingsController.cs b/AsbCloudWebApi/Controllers/UserSettingsController.cs
index e3e47327..db3f6f2e 100644
--- a/AsbCloudWebApi/Controllers/UserSettingsController.cs
+++ b/AsbCloudWebApi/Controllers/UserSettingsController.cs
@@ -55,6 +55,7 @@ namespace AsbCloudWebApi.Controllers
///
///
[HttpPut("{key}")]
+ [ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public virtual async Task> UpsertAsync(string key, [FromBody] System.Text.Json.JsonDocument value, CancellationToken token)
{
var userId = User.GetUserId();
@@ -63,7 +64,7 @@ namespace AsbCloudWebApi.Controllers
var result = await service.UpsertAsync((int)userId, key, value, token).ConfigureAwait(false);
if (result < 0)
- return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(key), "not found"));
+ throw new ArgumentInvalidException("not found", nameof(key));
return Ok(result);
}
@@ -74,6 +75,7 @@ namespace AsbCloudWebApi.Controllers
///
///
[HttpDelete("{key}")]
+ [ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
public virtual async Task> DeleteAsync(string key, CancellationToken token)
{
var userId = User.GetUserId();
@@ -82,7 +84,7 @@ namespace AsbCloudWebApi.Controllers
var result = await service.DeleteAsync((int)userId, key, token).ConfigureAwait(false);
if (result < 0)
- return BadRequest(ArgumentInvalidException.MakeValidationError(nameof(key), "not found"));
+ throw new ArgumentInvalidException("not found", nameof(key));
return Ok(result);
}
diff --git a/AsbCloudWebApi/Extentions.cs b/AsbCloudWebApi/Extentions.cs
index 021594fc..ba87612f 100644
--- a/AsbCloudWebApi/Extentions.cs
+++ b/AsbCloudWebApi/Extentions.cs
@@ -1,6 +1,7 @@
using AsbCloudApp.Data.User;
using AsbCloudWebApi.Converters;
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Claims;
@@ -30,13 +31,26 @@ namespace Microsoft.AspNetCore.Mvc
: null;
}
- public static IActionResult MakeBadRequest(this ControllerBase controller, string paramName, params string[] errors)
+ ///
+ ///
+ /// Returns BadRequest with ValidationProblemDetails as body
+ ///
+ ///
+ /// Используйте этот метод только если валидацию нельзя сделать через
+ /// атрибуты валидации или IValidatableObject модели.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static BadRequestObjectResult ValidationBadRequest(this ControllerBase controller, string paramName, string error)
{
- return controller.BadRequest(new
- {
- name = paramName,
- errors,
- });
+ var errors = new Dictionary {
+ { paramName, new[]{ error } }
+ };
+ var problem = new ValidationProblemDetails(errors);
+ return controller.BadRequest(problem);
}
public static MvcOptions UseDateOnlyTimeOnlyStringConverters(this MvcOptions options)
diff --git a/AsbCloudWebApi/Middlewares/SimplifyExceptionsMiddleware.cs b/AsbCloudWebApi/Middlewares/SimplifyExceptionsMiddleware.cs
index f7ff53e8..3180f41d 100644
--- a/AsbCloudWebApi/Middlewares/SimplifyExceptionsMiddleware.cs
+++ b/AsbCloudWebApi/Middlewares/SimplifyExceptionsMiddleware.cs
@@ -1,8 +1,9 @@
// Ignore Spelling: Middlewares
-
using AsbCloudApp.Exceptions;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Middlewares
@@ -55,8 +56,11 @@ namespace AsbCloudWebApi.Middlewares
private static string MakeJsonBody(ArgumentInvalidException ex)
{
- object error = ex.ToValidationErrorObject();
- var buffer = System.Text.Json.JsonSerializer.Serialize(error);
+ var errors = new Dictionary {
+ { ex.ParamName, new[]{ ex.Message } }
+ };
+ var problem = new ValidationProblemDetails(errors);
+ var buffer = System.Text.Json.JsonSerializer.Serialize(problem);
return buffer;
}
}