Настройка в AddSwaggerGen cтрокового формата данных для DateOnly

This commit is contained in:
Olga Nemt 2023-03-30 12:57:32 +05:00
parent 7fdb47e4cc
commit 6f08629966
11 changed files with 108 additions and 95 deletions

View File

@ -29,7 +29,7 @@ namespace AsbCloudApp.Data.DailyReport
/// <summary>
/// дата рапорта
/// </summary>
public DateTime ReportDate { get; set; }
public DateOnly ReportDate { get; set; }
/// <summary>
/// глубина забоя на дату начала интервала

View File

@ -20,7 +20,7 @@ namespace AsbCloudApp.Services
/// <param name="end"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IEnumerable<DailyReportDto>> GetListAsync(int idWell, DateTime? begin, DateTime? end, CancellationToken cancellationToken);
Task<IEnumerable<DailyReportDto>> GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken cancellationToken);
/// <summary>
/// Добавить новый рапорт
@ -30,7 +30,7 @@ namespace AsbCloudApp.Services
/// <param name="idUser"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> AddAsync(int idWell, DateTime startDate, int idUser, CancellationToken token);
Task<int> AddAsync(int idWell, DateOnly startDate, int idUser, CancellationToken token);
/// <summary>
/// Сформировать файл рапорта
@ -39,7 +39,7 @@ namespace AsbCloudApp.Services
/// <param name="date"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<Stream?> MakeReportAsync(int idWell, DateTime date, CancellationToken token);
Task<Stream?> MakeReportAsync(int idWell, DateOnly date, CancellationToken token);
/// <summary>
/// изменить блок данных для суточного рапорта
@ -49,6 +49,6 @@ namespace AsbCloudApp.Services
/// <param name="dto"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> UpdateBlockAsync(int idWell, DateTime startDate, ItemInfoDto dto, CancellationToken token);
Task<int> UpdateBlockAsync(int idWell, DateOnly startDate, ItemInfoDto dto, CancellationToken token);
}
}

View File

@ -5,6 +5,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@ -258,4 +260,17 @@ namespace AsbCloudDb
}
}
public class DateOnlyJsonConverter : JsonConverter<DateOnly>
{
public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateOnly.FromDateTime(reader.GetDateTime());
}
public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options)
{
var isoDate = value.ToString("O");
writer.WriteStringValue(isoDate);
}
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Text.Json.Serialization;
namespace AsbCloudDb.Model.DailyReport
{
public class Head : ItemInfo
@ -26,7 +28,8 @@ namespace AsbCloudDb.Model.DailyReport
/// <summary>
/// дата рапорта
/// </summary>
public DateTime ReportDate { get; set; }
[JsonConverter(typeof(DateOnlyJsonConverter))]
public DateOnly ReportDate { get; set; }
/// <summary>
/// глубина забоя на дату начала интервала

View File

@ -39,7 +39,7 @@ namespace AsbCloudInfrastructure.Services.DailyReport
}
public async Task<IEnumerable<DailyReportDto>> GetListAsync(int idWell, DateTime? begin, DateTime? end, CancellationToken token)
public async Task<IEnumerable<DailyReportDto>> GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken token)
{
var well = wellService.GetOrDefault(idWell);
if (well is null || well.Timezone is null)
@ -49,14 +49,12 @@ namespace AsbCloudInfrastructure.Services.DailyReport
if (begin is not null)
{
var beginDateOnly = ExtractDate(begin.Value, well);
query = query.Where(d => d.StartDate >= beginDateOnly);
query = query.Where(d => d.StartDate >= begin);
}
if (end is not null)
{
var endDateOnly = ExtractDate(end.Value, well);
query = query.Where(d => d.StartDate <= endDateOnly);
query = query.Where(d => d.StartDate <= end);
}
var entities = await query.OrderByDescending(e => e.StartDate)
@ -89,23 +87,22 @@ namespace AsbCloudInfrastructure.Services.DailyReport
return factOperations;
}
public async Task<int> AddAsync(int idWell, DateTime startDate, int idUser, CancellationToken token)
public async Task<int> AddAsync(int idWell, DateOnly startDate, int idUser, CancellationToken token)
{
var well = wellService.GetOrDefault(idWell);
if (well is null)
throw new ArgumentInvalidException("idWell doesn`t exist", nameof(idWell));
var startDateOnly = ExtractDate(startDate, well);
var hasEntity = await db.DailyReports
.AnyAsync(r => r.IdWell == idWell && r.StartDate == startDateOnly, token);
.AnyAsync(r => r.IdWell == idWell && r.StartDate == startDate, token);
if (hasEntity)
throw new ArgumentInvalidException($"daily report on {startDateOnly} already exists", nameof(startDateOnly));
throw new ArgumentInvalidException($"daily report on {startDate} already exists", nameof(startDate));
var entity = new AsbCloudDb.Model.DailyReport.DailyReport
{
IdWell = idWell,
StartDate = startDateOnly,
StartDate = startDate,
Info = new DailyReportInfo()
{
Head = CreateHeadDailyReportBlock(well, startDate, idUser)
@ -116,14 +113,13 @@ namespace AsbCloudInfrastructure.Services.DailyReport
return result;
}
public async Task<int> UpdateBlockAsync(int idWell, DateTime startDate, ItemInfoDto dto, CancellationToken token)
public async Task<int> UpdateBlockAsync(int idWell, DateOnly startDate, ItemInfoDto dto, CancellationToken token)
{
var well = wellService.GetOrDefault(idWell);
if (well is null)
throw new ArgumentInvalidException("idWell doesn`t exist", nameof(idWell));
var startDateOnly = DateOnly.FromDateTime(startDate);
var entity = await db.DailyReports.FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == startDateOnly, token);
var entity = await db.DailyReports.FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == startDate, token);
if (entity is null)
throw new ArgumentInvalidException("Daily report doesn`t exist", nameof(startDate));
@ -145,7 +141,7 @@ namespace AsbCloudInfrastructure.Services.DailyReport
return result;
}
public async Task<Stream?> MakeReportAsync(int idWell, DateTime date, CancellationToken token)
public async Task<Stream?> MakeReportAsync(int idWell, DateOnly date, CancellationToken token)
{
var stageIds = WellOperationCategory.WorkStages.Select(w => w.Id).ToArray();
var wellOperationCategories = wellOperationRepository.GetCategories(true)
@ -161,14 +157,10 @@ namespace AsbCloudInfrastructure.Services.DailyReport
return memoryStream;
}
private async Task<DailyReportDto?> GetOrDefaultAsync(int idWell, DateTime date, CancellationToken token)
private async Task<DailyReportDto?> GetOrDefaultAsync(int idWell, DateOnly date, CancellationToken token)
{
var dateOffset = date.Date;
var entity = await db.DailyReports
.FirstOrDefaultAsync(r => r.IdWell == idWell &&
r.StartDate.Year == dateOffset.Year &&
r.StartDate.DayOfYear == dateOffset.DayOfYear
, token);
.FirstOrDefaultAsync(r => r.IdWell == idWell && r.StartDate == date, token);
if (entity is null)
throw new ArgumentInvalidException("Daily report doesn`t exist", nameof(date));
@ -222,19 +214,6 @@ namespace AsbCloudInfrastructure.Services.DailyReport
return dto;
}
/// <summary>
/// Приведение данных к формату DateOnly с учетом часового пояса скважины
/// </summary>
/// <param name="dateTime"></param>
/// <param name="well"></param>
/// <returns></returns>
private DateOnly ExtractDate(DateTime dateTime, WellDto well)
{
var dateTimeOffset = dateTime.ToUtcDateTimeOffset(well!.Timezone.Hours);
var date = new DateOnly(dateTimeOffset.Year, dateTimeOffset.Month, dateTimeOffset.Day);
return date;
}
/// <summary>
/// Создание блока "Заголовок" по умолчанию
/// </summary>
@ -242,14 +221,14 @@ namespace AsbCloudInfrastructure.Services.DailyReport
/// <param name="startDate"></param>
/// <param name="idUser"></param>
/// <returns></returns>
private Head CreateHeadDailyReportBlock(WellDto well, DateTime startDate, int idUser)
private Head CreateHeadDailyReportBlock(WellDto well, DateOnly startDate, int idUser)
{
var customer = well.Companies.FirstOrDefault(company => company.IdCompanyType == 1);
var contractor = well.Companies.FirstOrDefault(company => company.IdCompanyType == 2);
return new Head()
{
ReportDate = startDate.Date,
ReportDate = startDate,
WellName = well.Caption,
ClusterName = well?.Cluster ?? string.Empty,
Customer = customer?.Caption ?? string.Empty,

View File

@ -45,7 +45,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<DailyReportDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetListAsync(int idWell, DateTime? begin, DateTime? end, CancellationToken token)
public async Task<IActionResult> GetListAsync(int idWell, DateOnly? begin, DateOnly? end, CancellationToken token)
{
var result = await dailyReportService.GetListAsync(idWell, begin, end, token);
return Ok(result);
@ -60,7 +60,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpPost]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> AddAsync(int idWell, [Required] DateTime startDate, CancellationToken token)
public async Task<IActionResult> AddAsync(int idWell, [Required] DateOnly startDate, CancellationToken token)
{
if (!await UserHasAccesToWellAsync(idWell, token))
return Forbid();
@ -81,7 +81,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpPut("{date}/head")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public Task<IActionResult> UpdateHeadAsync(int idWell, [Required] DateTime date, [Required] HeadDto dto, CancellationToken token)
public Task<IActionResult> UpdateHeadAsync(int idWell, [Required] DateOnly date, [Required] HeadDto dto, CancellationToken token)
=> UpdateReportBlockAsync(idWell, date, dto, token);
/// <summary>
@ -94,7 +94,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpPut("{date}/bha")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public Task<IActionResult> UpdateBhaAsync(int idWell, [Required] DateTime date, [Required] BhaDto dto, CancellationToken token)
public Task<IActionResult> UpdateBhaAsync(int idWell, [Required] DateOnly date, [Required] BhaDto dto, CancellationToken token)
=> UpdateReportBlockAsync(idWell, date, dto, token);
/// <summary>
@ -107,7 +107,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpPut("{date}/noDrilling")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public Task<IActionResult> UpdateNoDrillingAsync(int idWell, [Required] DateTime date, [Required] NoDrillingDto dto, CancellationToken token)
public Task<IActionResult> UpdateNoDrillingAsync(int idWell, [Required] DateOnly date, [Required] NoDrillingDto dto, CancellationToken token)
=> UpdateReportBlockAsync(idWell, date, dto, token);
/// <summary>
@ -120,7 +120,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpPut("{date}/saub")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public Task<IActionResult> UpdateSaubAsync(int idWell, [Required] DateTime date, [Required] SaubDto dto, CancellationToken token)
public Task<IActionResult> UpdateSaubAsync(int idWell, [Required] DateOnly date, [Required] SaubDto dto, CancellationToken token)
=> UpdateReportBlockAsync(idWell, date, dto, token);
/// <summary>
@ -133,7 +133,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpPut("{date}/sign")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public Task<IActionResult> UpdateSignAsync(int idWell, [Required] DateTime date, [Required] SignDto dto, CancellationToken token)
public Task<IActionResult> UpdateSignAsync(int idWell, [Required] DateOnly date, [Required] SignDto dto, CancellationToken token)
=> UpdateReportBlockAsync(idWell, date, dto, token);
/// <summary>
@ -144,7 +144,7 @@ namespace AsbCloudWebApi.Controllers
/// <param name="dto"></param>
/// <param name="token"></param>
/// <returns></returns>
private async Task<IActionResult> UpdateReportBlockAsync(int idWell, DateTime date, ItemInfoDto dto, CancellationToken token)
private async Task<IActionResult> UpdateReportBlockAsync(int idWell, DateOnly date, ItemInfoDto dto, CancellationToken token)
{
if (!await UserHasAccesToWellAsync(idWell, token))
return Forbid();
@ -165,7 +165,7 @@ namespace AsbCloudWebApi.Controllers
/// <returns></returns>
[HttpGet("{date}/excel")]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> DownloadAsync(int idWell, DateTime date, CancellationToken token)
public async Task<IActionResult> DownloadAsync(int idWell, DateOnly date, CancellationToken token)
{
if (!await UserHasAccesToWellAsync(idWell, token))
return Forbid();

View File

@ -0,0 +1,48 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AsbCloudWebApi.Converters
{
#nullable enable
public class DateOnlyTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
if (value is string str)
{
return DateOnly.Parse(str);
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
{
if (destinationType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (destinationType == typeof(string) && value is DateOnly date)
{
return date.ToString("O");
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
#nullable disable
}

View File

@ -5,7 +5,7 @@ using System.Globalization;
namespace AsbCloudWebApi.Converters
{
#nullable enable
public class DateOnlyTypeConverter : TypeConverter
public class TimeOnlyTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{

View File

@ -3,11 +3,9 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@ -19,6 +17,7 @@ namespace AsbCloudWebApi
{
services.AddSwaggerGen(c =>
{
c.MapType<DateOnly>(() => new OpenApiSchema { Type = "string", Format = "date" });
c.CustomOperationIds(e =>
{
return $"{e.ActionDescriptor.RouteValues["action"]}";

View File

@ -1,9 +1,10 @@
using AsbCloudApp.Data;
using System.Collections.Generic;
using System.Linq;
using AsbCloudWebApi.Converters;
using System;
using System.ComponentModel;
using System.Security.Claims;
namespace Microsoft.AspNetCore.Mvc
namespace Microsoft.AspNetCore.Mvc
{
public static class Extentions
{
@ -38,48 +39,12 @@ namespace Microsoft.AspNetCore.Mvc
});
}
public static BadRequestBuilder BadRequestBuilder(this ControllerBase controller, string paramName, params string[] errors)
=> new BadRequestBuilder(paramName, errors);
}
public class BadRequestBuilder
{
private readonly Dictionary<string, List<string>> body;
private List<string> GetOrCreateNew(string paramName)
public static MvcOptions UseDateOnlyTimeOnlyStringConverters(this MvcOptions options)
{
List<string> par;
if (body.ContainsKey(paramName))
par = body[paramName];
else
{
par = new List<string>();
body[paramName] = par;
}
return par;
TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter)));
return options;
}
public BadRequestBuilder(string paramName, params string[] errors)
{
body = new();
body[paramName] = new List<string>(errors);
}
public BadRequestBuilder Add(string paramName, params string[] errors)
{
var par = GetOrCreateNew(paramName);
par.AddRange(errors);
return this;
}
public BadRequestObjectResult Build()
{
var o = body.Select(e => new { name = e.Key, errors = e.Value.ToArray() });
return new BadRequestObjectResult(o);
}
public static implicit operator BadRequestObjectResult(BadRequestBuilder d) => d.Build();
}
}

View File

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi
{
@ -31,6 +32,8 @@ namespace AsbCloudWebApi
}))
.AddProtoBufNet();
services.AddControllers(options => options.UseDateOnlyTimeOnlyStringConverters());
ProtobufModel.EnshureRegistered();
services.AddSwagger();
@ -90,6 +93,7 @@ namespace AsbCloudWebApi
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)