diff --git a/AsbCloudApp/Data/MessageDto.cs b/AsbCloudApp/Data/MessageDto.cs index c3f5def4..0cdc70c4 100644 --- a/AsbCloudApp/Data/MessageDto.cs +++ b/AsbCloudApp/Data/MessageDto.cs @@ -22,10 +22,6 @@ namespace AsbCloudApp.Data [Range(1, int.MaxValue, ErrorMessage = "Id категории не может быть ниже 1")] public int CategoryId { get; set; } - //TODO: в модели дто сообщения отсутствует поле Id скважины - // скорее всего опечатка т.к. используется глубина в правиле валидатора - //в других валидаторах парамтр глубины идет рэнжированный от...до - /// /// глубина забоя, при котором событие возникло /// diff --git a/AsbCloudApp/Services/IScheduleService.cs b/AsbCloudApp/Services/IScheduleService.cs index 832c2839..9a77b7b9 100644 --- a/AsbCloudApp/Services/IScheduleService.cs +++ b/AsbCloudApp/Services/IScheduleService.cs @@ -10,7 +10,6 @@ namespace AsbCloudApp.Services /// public interface IScheduleRepository : IRepositoryWellRelated { - // TODO: this should be nullable. /// /// получить бурильщика по idWell и времени /// @@ -18,6 +17,6 @@ namespace AsbCloudApp.Services /// /// /// - Task GetDrillerAsync(int idWell, DateTime workTime, CancellationToken token); + Task GetOrDefaultDrillerAsync(int idWell, DateTime workTime, CancellationToken token); } } diff --git a/AsbCloudApp/Services/ITelemetryDataService.cs b/AsbCloudApp/Services/ITelemetryDataService.cs index af80eb23..20ac7f90 100644 --- a/AsbCloudApp/Services/ITelemetryDataService.cs +++ b/AsbCloudApp/Services/ITelemetryDataService.cs @@ -22,7 +22,7 @@ namespace AsbCloudApp.Services /// кол-во элементов до которых эти данные прореживаются /// /// - Task?> GetOrDefaultAsync(int idWell, + Task> GetAsync(int idWell, DateTime dateBegin = default, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); diff --git a/AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs b/AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs new file mode 100644 index 00000000..3ad6fce0 --- /dev/null +++ b/AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs @@ -0,0 +1,80 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace AsbCloudApp.ValidationAttributes +{ + /// + /// Атрибут валидации даты-времени + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public class DateValidationAttribute : ValidationAttribute + { + private DateTime? gtDate; + + /// + /// null разрешен + /// + public bool AllowNull { get; set; } = true; + + /// + /// Разрешена только дат. + /// При наличии времени в DateTime инвалидирует. + /// При наличии UTC тоже инвалидирует. + /// + public bool IsDateOnly { get; set; } = false; + + /// + /// Допустима дата-время в UTC + /// + public bool AllowUtc { get; set; } = true; + + /// + /// Дата больше которой должно быть проверяемое значение. + /// Формат строки - любой поддерживаемый DateTime.Parse. + /// Желательно использовать ISO 8601 формат + /// + public string? GtDate { + get => gtDate.ToString(); + set + { + if(value is null) + gtDate = null; + else + gtDate = DateTime.Parse(value); + } + } + + /// + /// Проверка значения + /// + /// + /// + public override bool IsValid(object? value) + { + if (value is null) + return AllowNull; + + if (value is not DateTime dateTime) + return false; + + if (IsDateOnly) + { + if (dateTime.Hour > 0 || + dateTime.Minute > 0 || + dateTime.Second > 0 || + dateTime.Millisecond > 0 || + dateTime.Kind == DateTimeKind.Utc) + return false; + } + + if (!AllowUtc && dateTime.Kind == DateTimeKind.Utc) + return false; + + if (gtDate.HasValue && dateTime <= gtDate) + return false; + + return true; + } + + } +} diff --git a/AsbCloudInfrastructure/Repository/ScheduleRepository.cs b/AsbCloudInfrastructure/Repository/ScheduleRepository.cs index b43abe98..856d547e 100644 --- a/AsbCloudInfrastructure/Repository/ScheduleRepository.cs +++ b/AsbCloudInfrastructure/Repository/ScheduleRepository.cs @@ -21,7 +21,7 @@ namespace AsbCloudInfrastructure.Repository this.wellService = wellService; } - public async Task GetDrillerAsync(int idWell, DateTime workTime, CancellationToken token) + public async Task GetOrDefaultDrillerAsync(int idWell, DateTime workTime, CancellationToken token) { var hoursOffset = wellService.GetTimezone(idWell).Hours; var date = workTime.ToUtcDateTimeOffset(hoursOffset); diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs index 6da87bc3..23a65555 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs @@ -86,16 +86,14 @@ namespace AsbCloudInfrastructure.Services.SAUB } } - - // TODO: It shouldn`t be nullable. Throw exceptions instead and return empty. /// - public virtual async Task?> GetOrDefaultAsync(int idWell, + public virtual async Task> GetAsync(int idWell, DateTime dateBegin = default, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); if (telemetry is null) - return null; + return Enumerable.Empty(); var timezone = telemetryService.GetTimezone(telemetry.Id); @@ -134,7 +132,7 @@ namespace AsbCloudInfrastructure.Services.SAUB .ConfigureAwait(false); if (fullDataCount == 0) - return default; + return Enumerable.Empty(); if (fullDataCount > 1.75 * approxPointsCount) { diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs index 88ba617d..0d34b80c 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs @@ -121,8 +121,7 @@ namespace AsbCloudInfrastructure.Services.SAUB _ => 32_768 }; - var data = await GetOrDefaultAsync(idWell, beginDate, intervalSec, approxPointsCount, token ) - ?? Enumerable.Empty(); + var data = await GetAsync(idWell, beginDate, intervalSec, approxPointsCount, token ); var fileName = $"DataSaub idWell{idWell}"; if (telemetry.Info is not null) diff --git a/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs b/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs new file mode 100644 index 00000000..3e4990a2 --- /dev/null +++ b/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs @@ -0,0 +1,108 @@ +using AsbCloudApp.ValidationAttributes; +using System; +using Xunit; + +namespace AsbCloudWebApi.Tests.AttributeTest +{ + public class DateValidationAttributeTest + { + [Fact] + public void AllowNull_true_on_null_valid() + { + var attribute = new DateValidationAttribute { AllowNull = true }; + var result = attribute.IsValid(null); + Assert.True(result); + } + + [Fact] + public void AllowNull_false_on_null_invalid() + { + var attribute = new DateValidationAttribute { AllowNull = false }; + var result = attribute.IsValid(null); + Assert.False(result); + } + + [Fact] + public void IsDateOnly_true_on_empty_timePart_valid() + { + var attribute = new DateValidationAttribute { IsDateOnly = true }; + var date = new DateTime(2023, 01, 01, 00, 00, 00); + var result = attribute.IsValid(date); + Assert.True(result); + } + + [Fact] + public void IsDateOnly_true_on_timePart_invalid() + { + var attribute = new DateValidationAttribute { IsDateOnly = true }; + var date = new DateTime(2023, 01, 01, 01, 01, 01); + var result = attribute.IsValid(date); + Assert.False(result); + } + + [Fact] + public void IsDateOnly_true_on_utc_invalid() + { + var attribute = new DateValidationAttribute { IsDateOnly = true }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Utc); + var result = attribute.IsValid(date); + Assert.False(result); + } + + [Fact] + public void AllowUtc_true_on_unspecified_valid() + { + var attribute = new DateValidationAttribute { AllowUtc = true }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Unspecified); + var result = attribute.IsValid(date); + Assert.True(result); + } + + [Fact] + public void AllowUtc_true_on_utc_valid() + { + var attribute = new DateValidationAttribute { AllowUtc = true }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Utc); + var result = attribute.IsValid(date); + Assert.True(result); + } + + [Fact] + public void AllowUtc_false_on_utc_invalid() + { + var attribute = new DateValidationAttribute { AllowUtc = false }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Utc); + var result = attribute.IsValid(date); + Assert.False(result); + } + + [Fact] + public void AllowUtc_false_on_unspecified_valid() + { + var attribute = new DateValidationAttribute { AllowUtc = false }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Unspecified); + var result = attribute.IsValid(date); + Assert.True(result); + } + + [Fact] + public void GtDate_on_same_date_invalid() + { + var gtDate = "2023-01-01T00:00:00"; + var date = DateTime.Parse(gtDate); + var attribute = new DateValidationAttribute { GtDate = gtDate }; + var result = attribute.IsValid(date); + Assert.False(result); + } + + [Fact] + public void GtDate_on_greater_date_valid() + { + var gtDate = "2023-01-01T00:00:00"; + var date = DateTime.Parse(gtDate).AddMilliseconds(1); + var attribute = new DateValidationAttribute { GtDate = gtDate }; + var result = attribute.IsValid(date); + Assert.True(result); + } + } +} diff --git a/AsbCloudWebApi.Tests/Middlware/UserConnectionsLimitMiddlwareTest.cs b/AsbCloudWebApi.Tests/Middlware/UserConnectionsLimitMiddlwareTest.cs index 78abcb1d..96556845 100644 --- a/AsbCloudWebApi.Tests/Middlware/UserConnectionsLimitMiddlwareTest.cs +++ b/AsbCloudWebApi.Tests/Middlware/UserConnectionsLimitMiddlwareTest.cs @@ -35,7 +35,7 @@ namespace AsbCloudWebApi.Tests.Middlware public class TelemetryDataSaubService : ITelemetryDataSaubService { - public async Task?> GetOrDefaultAsync(int idWell, DateTime dateBegin = default, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default) + public async Task?> GetAsync(int idWell, DateTime dateBegin = default, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default) { await Task.Delay(1000, token); return Enumerable.Empty(); diff --git a/AsbCloudWebApi/Controllers/OperationStatController.cs b/AsbCloudWebApi/Controllers/OperationStatController.cs index 72856a33..cce509fb 100644 --- a/AsbCloudWebApi/Controllers/OperationStatController.cs +++ b/AsbCloudWebApi/Controllers/OperationStatController.cs @@ -77,22 +77,17 @@ namespace AsbCloudWebApi.Controllers /// /// [HttpGet] - [Route("cluster/{idCluster}/stat")] // TODO: Это статистика кластера, перенести в ClusterOperationStatController + [Route("cluster/{idCluster}/stat")] [Permission] [ProducesResponseType(typeof(StatClusterDto), (int)System.Net.HttpStatusCode.OK)] public async Task GetStatClusterAsync(int idCluster, CancellationToken token = default) { - int? idCompanyOrNull = User.GetCompanyId(); - if (idCompanyOrNull is null) + int? idCompany = User.GetCompanyId(); + if (idCompany is null) return Forbid(); - int idCompany = idCompanyOrNull ?? 0; - // TODO: Fix next commented lines - //if (!await CanUserAccessToWellAsync(idCluster, token).ConfigureAwait(false)) - // return Forbid(); - - var result = await operationsStatService.GetStatClusterAsync(idCluster, idCompany, token) + var result = await operationsStatService.GetStatClusterAsync(idCluster, idCompany.Value, token) .ConfigureAwait(false); return Ok(result); } diff --git a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs index 7b210d71..b9c81731 100644 --- a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs @@ -86,7 +86,7 @@ namespace AsbCloudWebApi.Controllers.SAUB if (!isCompanyOwnsWell) return Forbid(); - var content = await telemetryDataService.GetOrDefaultAsync(idWell, begin, + var content = await telemetryDataService.GetAsync(idWell, begin, intervalSec, approxPointsCount, token).ConfigureAwait(false); return Ok(content); diff --git a/AsbCloudWebApi/Controllers/ScheduleController.cs b/AsbCloudWebApi/Controllers/ScheduleController.cs index e0c0752a..8df91cce 100644 --- a/AsbCloudWebApi/Controllers/ScheduleController.cs +++ b/AsbCloudWebApi/Controllers/ScheduleController.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; namespace AsbCloudWebApi.Controllers { +#nullable enable /// /// Расписание бурильщиков /// @@ -32,12 +33,12 @@ namespace AsbCloudWebApi.Controllers /// /// бурильщик [HttpGet("driller")] - public async Task> GetDrillerAsync(int idWell, DateTime workTime, CancellationToken token) + public async Task> GetDrillerAsync(int idWell, DateTime workTime, CancellationToken token) { if (!await UserHasAccesToWellAsync(idWell, token)) return Forbid(); - var result = await scheduleService.GetDrillerAsync(idWell, workTime, token); + var result = await scheduleService.GetOrDefaultDrillerAsync(idWell, workTime, token); return Ok(result); }