Compare commits
39 Commits
Author | SHA1 | Date | |
---|---|---|---|
fdf49b91ab | |||
|
a0f5afb923 | ||
|
3bb5fc4411 | ||
|
a07dbae5b8 | ||
|
2169e592e6 | ||
|
02def3a7d6 | ||
f5ed62eb7d | |||
f31805a769 | |||
b79d29f08e | |||
23e2f86957 | |||
61dd5110bd | |||
afe7310f73 | |||
4fa00de88e | |||
c496cea07a | |||
3806e395eb | |||
c1e30a8834 | |||
|
bd8de9afc2 | ||
|
b75714c835 | ||
f6648b812d | |||
1fdd199954 | |||
c751f74b35 | |||
6518aeabf1 | |||
d9cbc9022d | |||
098c180b12 | |||
b7806493a2 | |||
3a1ea55be2 | |||
7d5370bf43 | |||
9e55d6791c | |||
e5ffd42fb3 | |||
4d24eb9445 | |||
b5e255b940 | |||
5f2535b517 | |||
9e375b4831 | |||
531b14938f | |||
6fd79f6c4a | |||
90c62b9ede | |||
ef72d985be | |||
d746f85fe4 | |||
84e7ec274c |
@ -1,68 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Persistence.Models;
|
|
||||||
using Persistence.Repositories;
|
|
||||||
|
|
||||||
namespace Persistence.API.Controllers
|
|
||||||
{
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class ChangeLogController<TDto, TChangeLogDto> : ControllerBase, IChangeLogApi<TDto, TChangeLogDto>
|
|
||||||
where TDto : class, IChangeLogAbstract, new()
|
|
||||||
where TChangeLogDto : ChangeLogDto<TDto>
|
|
||||||
{
|
|
||||||
private IChangeLogRepository<TDto, TChangeLogDto> changeLogRepository;
|
|
||||||
|
|
||||||
public ChangeLogController(IChangeLogRepository<TDto, TChangeLogDto> changeLogRepository)
|
|
||||||
{
|
|
||||||
this.changeLogRepository = changeLogRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public Task<ActionResult<IEnumerable<TDto>>> GetChangeLogCurrent(CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("forDate")]
|
|
||||||
public Task<ActionResult<IEnumerable<TChangeLogDto>>> GetChangeLogForDate(DateTimeOffset historyMoment, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public Task<ActionResult<int>> AddAsync(TDto dto, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public Task<ActionResult<int>> AddRangeAsync(IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public Task<ActionResult<int>> DeleteAsync(int id, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public Task<ActionResult<int>> DeleteRangeAsync(IEnumerable<int> ids, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("update")]
|
|
||||||
public Task<ActionResult<int>> UpdateAsync(TDto dto, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("update")]
|
|
||||||
public Task<ActionResult<int>> UpdateRangeAsync(IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Persistence.API;
|
|
||||||
using Persistence.Repositories;
|
using Persistence.Repositories;
|
||||||
using Persistence.Repository.Data;
|
using Persistence.Repository.Data;
|
||||||
|
|
||||||
namespace Persistence.API.Controllers;
|
namespace Persistence.API.Controllers;
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class DataSaubController : TimeSeriesController<DataSaubDto>
|
public class DataSaubController : TimeSeriesController<DataSaubDto>
|
||||||
{
|
{
|
||||||
|
53
Persistence.API/Controllers/SetpointController.cs
Normal file
53
Persistence.API/Controllers/SetpointController.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Repositories;
|
||||||
|
|
||||||
|
namespace Persistence.API.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class SetpointController : ControllerBase, ISetpointApi
|
||||||
|
{
|
||||||
|
private readonly ISetpointRepository setpointRepository;
|
||||||
|
|
||||||
|
public SetpointController(ISetpointRepository setpointRepository)
|
||||||
|
{
|
||||||
|
this.setpointRepository = setpointRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("current")]
|
||||||
|
public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetCurrent([FromQuery] IEnumerable<Guid> setpointKeys, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await setpointRepository.GetCurrent(setpointKeys, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("history")]
|
||||||
|
public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetHistory([FromQuery] IEnumerable<Guid> setpointKeys, [FromQuery] DateTimeOffset historyMoment, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await setpointRepository.GetHistory(setpointKeys, historyMoment, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("log")]
|
||||||
|
public async Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([FromQuery] IEnumerable<Guid> setpointKeys, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await setpointRepository.GetLog(setpointKeys, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token)
|
||||||
|
{
|
||||||
|
// ToDo: вычитка idUser
|
||||||
|
await setpointRepository.Save(setpointKey, newValue, 0, token);
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
using Persistence.Repositories;
|
using Persistence.Repositories;
|
||||||
|
|
||||||
namespace Persistence.API.Controllers;
|
namespace Persistence.API.Controllers;
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDto>
|
public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDto>
|
||||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||||
@ -17,22 +19,32 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public async Task<IActionResult> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
|
public async Task<IActionResult> Get(DateTimeOffset dateBegin, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await this.timeSeriesDataRepository.GetAsync(dateBegin, dateEnd, token);
|
var result = await this.timeSeriesDataRepository.GetGtDate(dateBegin, token);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("datesRange")]
|
[HttpGet("datesRange")]
|
||||||
public Task<IActionResult> GetDatesRangeAsync(CancellationToken token)
|
public async Task<IActionResult> GetDatesRange(CancellationToken token)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var result = await this.timeSeriesDataRepository.GetDatesRange(token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("resampled")]
|
||||||
|
public async Task<IActionResult> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var result = await this.timeSeriesDataRepository.GetResampledData(dateBegin, intervalSec, approxPointsCount, token);
|
||||||
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> InsertRangeAsync(IEnumerable<TDto> dtos, CancellationToken token)
|
public async Task<IActionResult> InsertRange(IEnumerable<TDto> dtos, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await this.timeSeriesDataRepository.InsertRange(dtos, token);
|
var result = await this.timeSeriesDataRepository.InsertRange(dtos, token);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
104
Persistence.API/Controllers/TimestampedSetController.cs
Normal file
104
Persistence.API/Controllers/TimestampedSetController.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Repositories;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Persistence.API.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Хранение наборов данных с отметкой времени.
|
||||||
|
/// Не оптимизировано под большие данные.
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("api/[controller]/{idDiscriminator}")]
|
||||||
|
public class TimestampedSetController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ITimestampedSetRepository repository;
|
||||||
|
|
||||||
|
public TimestampedSetController(ITimestampedSetRepository repository)
|
||||||
|
{
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Записать новые данные
|
||||||
|
/// Предполагается что данные с одним дискриминатором имеют одинаковую структуру
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="sets"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns>кол-во затронутых записей</returns>
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> InsertRange([FromRoute]Guid idDiscriminator, [FromBody]IEnumerable<TimestampedSetDto> sets, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.InsertRange(idDiscriminator, sets, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных с фильтрацией. Значение фильтра null - отключен
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="geTimestamp">Фильтр позднее даты</param>
|
||||||
|
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||||
|
/// <param name="skip"></param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns>Фильтрованный набор данных с сортировкой по отметке времени</returns>
|
||||||
|
[HttpGet]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<TimestampedSetDto>), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, [FromQuery]IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.Get(idDiscriminator, geTimestamp, columnNames, skip, take, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить последние данные
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns>Фильтрованный набор данных с сортировкой по отметке времени</returns>
|
||||||
|
[HttpGet("last")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<TimestampedSetDto>), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> GetLast(Guid idDiscriminator, [FromQuery]IEnumerable<string>? columnNames, int take, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.GetLast(idDiscriminator, columnNames, take, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Диапазон дат за которые есть данные
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns>Дата первой и последней записи</returns>
|
||||||
|
[HttpGet("datesRange")]
|
||||||
|
[ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
|
||||||
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
|
public async Task<IActionResult> GetDatesRange(Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.GetDatesRange(idDiscriminator, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Количество записей по указанному набору в БД. Для пагинации.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("count")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
|
public async Task<IActionResult> Count(Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.Count(idDiscriminator, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
195
Persistence.API/DependencyInjection.cs
Normal file
195
Persistence.API/DependencyInjection.cs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Microsoft.OpenApi.Any;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Persistence.Models.Configurations;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace Persistence.API;
|
||||||
|
|
||||||
|
public static class DependencyInjection
|
||||||
|
{
|
||||||
|
public static void AddSwagger(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
c.MapType<TimeSpan>(() => new OpenApiSchema { Type = "string", Example = new OpenApiString("0.00:00:00") });
|
||||||
|
c.MapType<DateOnly>(() => new OpenApiSchema { Type = "string", Format = "date" });
|
||||||
|
c.MapType<JsonValue>(() => new OpenApiSchema
|
||||||
|
{
|
||||||
|
AnyOf = new OpenApiSchema[]
|
||||||
|
{
|
||||||
|
new OpenApiSchema {Type = "string", Format = "string" },
|
||||||
|
new OpenApiSchema {Type = "number", Format = "int32" },
|
||||||
|
new OpenApiSchema {Type = "number", Format = "float" },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
c.CustomOperationIds(e =>
|
||||||
|
{
|
||||||
|
return $"{e.ActionDescriptor.RouteValues["action"]}";
|
||||||
|
});
|
||||||
|
|
||||||
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Persistence web api", Version = "v1" });
|
||||||
|
|
||||||
|
var needUseKeyCloak = configuration.GetSection("NeedUseKeyCloak").Get<bool>();
|
||||||
|
if (needUseKeyCloak)
|
||||||
|
c.AddKeycloackSecurity(configuration);
|
||||||
|
else c.AddDefaultSecurity(configuration);
|
||||||
|
|
||||||
|
//var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||||
|
//var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
||||||
|
//var includeControllerXmlComment = true;
|
||||||
|
//options.IncludeXmlComments(xmlPath, includeControllerXmlComment);
|
||||||
|
//options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "AsbCloudApp.xml"), includeControllerXmlComment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Authentication
|
||||||
|
public static void AddJWTAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var needUseKeyCloak = configuration
|
||||||
|
.GetSection("NeedUseKeyCloak")
|
||||||
|
.Get<bool>();
|
||||||
|
if (needUseKeyCloak)
|
||||||
|
services.AddKeyCloakAuthentication(configuration);
|
||||||
|
else services.AddDefaultAuthentication(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddKeyCloakAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.RequireHttpsMetadata = false;
|
||||||
|
options.Audience = configuration["Authentication:Audience"];
|
||||||
|
options.MetadataAddress = configuration["Authentication:MetadataAddress"]!;
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidIssuer = configuration["Authentication:ValidIssuer"],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddDefaultAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.RequireHttpsMetadata = false;
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidIssuer = JwtParams.Issuer,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidAudience = JwtParams.Audience,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
IssuerSigningKey = JwtParams.SecurityKey,
|
||||||
|
ValidateIssuerSigningKey = false
|
||||||
|
};
|
||||||
|
options.Events = new JwtBearerEvents
|
||||||
|
{
|
||||||
|
OnMessageReceived = context =>
|
||||||
|
{
|
||||||
|
var accessToken = context.Request.Headers["Authorization"]
|
||||||
|
.ToString()
|
||||||
|
.Replace(JwtBearerDefaults.AuthenticationScheme, string.Empty)
|
||||||
|
.Trim();
|
||||||
|
|
||||||
|
context.Token = accessToken;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
},
|
||||||
|
OnTokenValidated = context =>
|
||||||
|
{
|
||||||
|
var username = context.Principal?.Claims
|
||||||
|
.FirstOrDefault(e => e.Type == "username")?.Value;
|
||||||
|
|
||||||
|
var password = context.Principal?.Claims
|
||||||
|
.FirstOrDefault(e => e.Type == "password")?.Value;
|
||||||
|
|
||||||
|
var keyCloakUser = configuration
|
||||||
|
.GetSection(nameof(AuthUser))
|
||||||
|
.Get<AuthUser>()!;
|
||||||
|
|
||||||
|
if (username != keyCloakUser.Username || password != keyCloakUser.Password)
|
||||||
|
{
|
||||||
|
context.Fail("username or password did not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Security (Swagger)
|
||||||
|
private static void AddKeycloackSecurity(this SwaggerGenOptions options, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
options.AddSecurityDefinition("Keycloack", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345abcdef'",
|
||||||
|
Name = "Authorization",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Type = SecuritySchemeType.OAuth2,
|
||||||
|
Flows = new OpenApiOAuthFlows
|
||||||
|
{
|
||||||
|
Implicit = new OpenApiOAuthFlow
|
||||||
|
{
|
||||||
|
AuthorizationUrl = new Uri(configuration["Authentication:AuthorizationUrl"]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Type = ReferenceType.SecurityScheme,
|
||||||
|
Id = "Keycloack"
|
||||||
|
},
|
||||||
|
Scheme = "Bearer",
|
||||||
|
Name = "Bearer",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
},
|
||||||
|
new List<string>()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddDefaultSecurity(this SwaggerGenOptions options, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345abcdef'",
|
||||||
|
Name = "Authorization",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Type = SecuritySchemeType.ApiKey,
|
||||||
|
Scheme = "Bearer",
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Type = ReferenceType.SecurityScheme,
|
||||||
|
Id = "Bearer"
|
||||||
|
},
|
||||||
|
Scheme = "oauth2",
|
||||||
|
Name = "Bearer",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
},
|
||||||
|
new List<string>()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
26
Persistence.API/Extensions.cs
Normal file
26
Persistence.API/Extensions.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Persistence.API;
|
||||||
|
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static T GetUserId<T>(this ClaimsPrincipal principal)
|
||||||
|
{
|
||||||
|
if (principal == null)
|
||||||
|
throw new ArgumentNullException(nameof(principal));
|
||||||
|
|
||||||
|
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
|
||||||
|
if (String.IsNullOrEmpty(loggedInUserId))
|
||||||
|
throw new ArgumentNullException(nameof(loggedInUserId));
|
||||||
|
|
||||||
|
var result = TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(loggedInUserId);
|
||||||
|
|
||||||
|
if (result is null)
|
||||||
|
throw new ArgumentNullException(nameof(result));
|
||||||
|
|
||||||
|
return (T)result;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -8,11 +8,13 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Persistence.Database.Postgres\Persistence.Database.Postgres.csproj" />
|
||||||
<ProjectReference Include="..\Persistence.Repository\Persistence.Repository.csproj" />
|
<ProjectReference Include="..\Persistence.Repository\Persistence.Repository.csproj" />
|
||||||
<ProjectReference Include="..\Persistence\Persistence.csproj" />
|
<ProjectReference Include="..\Persistence\Persistence.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Persistence.Models;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Persistence.Repositories;
|
|
||||||
using Persistence.Repository;
|
|
||||||
using Persistence.Repository.Data;
|
|
||||||
using Persistence.Repository.Repositories;
|
|
||||||
|
|
||||||
namespace Persistence.API;
|
namespace Persistence.API;
|
||||||
|
|
||||||
@ -12,8 +7,9 @@ public class Program
|
|||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
|
||||||
var host = CreateHostBuilder(args).Build();
|
var host = CreateHostBuilder(args).Build();
|
||||||
Persistence.Repository.Startup.BeforeRunHandler(host);
|
Startup.BeforeRunHandler(host);
|
||||||
host.Run();
|
host.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
},
|
},
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"applicationUrl": "http://localhost:5032"
|
"applicationUrl": "http://localhost:13616"
|
||||||
},
|
},
|
||||||
"IIS Express": {
|
"IIS Express": {
|
||||||
"commandName": "IISExpress",
|
"commandName": "IISExpress",
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Persistence.Repository;
|
using Persistence.Repository;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Database.Postgres;
|
||||||
|
|
||||||
namespace Persistence.API;
|
namespace Persistence.API;
|
||||||
|
|
||||||
@ -17,9 +19,10 @@ public class Startup
|
|||||||
services.AddControllers();
|
services.AddControllers();
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
services.AddEndpointsApiExplorer();
|
services.AddEndpointsApiExplorer();
|
||||||
services.AddSwaggerGen();
|
services.AddSwagger(Configuration);
|
||||||
|
services.AddInfrastructure();
|
||||||
services.AddInfrastructure(Configuration);
|
services.AddPersistenceDbContext(Configuration);
|
||||||
|
services.AddJWTAuthentication(Configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
@ -30,11 +33,28 @@ public class Startup
|
|||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
//app.UseAuthorization();
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseEndpoints(endpoints =>
|
app.UseEndpoints(endpoints =>
|
||||||
{
|
{
|
||||||
endpoints.MapControllers();
|
endpoints.MapControllers();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void BeforeRunHandler(IHost host)
|
||||||
|
{
|
||||||
|
using var scope = host.Services.CreateScope();
|
||||||
|
var provider = scope.ServiceProvider;
|
||||||
|
|
||||||
|
var context = provider.GetRequiredService<PersistenceDbContext>();
|
||||||
|
context.Database.EnsureCreatedAndMigrated();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
namespace Persistence.API;
|
|
||||||
|
|
||||||
public class WeatherForecast
|
|
||||||
{
|
|
||||||
public DateOnly Date { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureC { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
|
|
||||||
public string? Summary { get; set; }
|
|
||||||
}
|
|
@ -4,5 +4,6 @@
|
|||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"NeedUseKeyCloak": false
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
{
|
{
|
||||||
"DbConnection": {
|
"DbConnection": {
|
||||||
"Host": "localhost",
|
"Host": "localhost",
|
||||||
"Port": 5432,
|
"Port": 5432,
|
||||||
"Username": "postgres",
|
"Username": "postgres",
|
||||||
"Password": "q"
|
"Password": "q"
|
||||||
}
|
},
|
||||||
|
"NeedUseKeyCloak": false,
|
||||||
|
"AuthUser": {
|
||||||
|
"username": "myuser",
|
||||||
|
"password": 12345,
|
||||||
|
"clientId": "webapi",
|
||||||
|
"grantType": "password"
|
||||||
|
},
|
||||||
|
"KeycloakGetTokenUrl": "http://192.168.0.10:8321/realms/Persistence/protocol/openid-connect/token"
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=q;Persist Security Info=True",
|
"DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=q;Persist Security Info=True"
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*",
|
||||||
|
"Authentication": {
|
||||||
|
"MetadataAddress": "http://192.168.0.10:8321/realms/Persistence/.well-known/openid-configuration",
|
||||||
|
"Audience": "account",
|
||||||
|
"ValidIssuer": "http://192.168.0.10:8321/realms/Persistence",
|
||||||
|
"AuthorizationUrl": "http://192.168.0.10:8321/realms/Persistence/protocol/openid-connect/auth"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
24
Persistence.Client/Clients/ISetpointClient.cs
Normal file
24
Persistence.Client/Clients/ISetpointClient.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using Persistence.Models;
|
||||||
|
using Refit;
|
||||||
|
|
||||||
|
namespace Persistence.Client.Clients;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Интерфейс для тестирования API, предназначенного для работы с уставками
|
||||||
|
/// </summary>
|
||||||
|
public interface ISetpointClient
|
||||||
|
{
|
||||||
|
private const string BaseRoute = "/api/setpoint";
|
||||||
|
|
||||||
|
[Get($"{BaseRoute}/current")]
|
||||||
|
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetCurrent([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys);
|
||||||
|
|
||||||
|
[Get($"{BaseRoute}/history")]
|
||||||
|
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetHistory([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys, [Query] DateTimeOffset historyMoment);
|
||||||
|
|
||||||
|
[Get($"{BaseRoute}/log")]
|
||||||
|
Task<IApiResponse<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys);
|
||||||
|
|
||||||
|
[Post($"{BaseRoute}/")]
|
||||||
|
Task<IApiResponse> Save(Guid setpointKey, object newValue);
|
||||||
|
}
|
22
Persistence.Client/Clients/ITimeSeriesClient.cs
Normal file
22
Persistence.Client/Clients/ITimeSeriesClient.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Refit;
|
||||||
|
|
||||||
|
namespace Persistence.Client.Clients;
|
||||||
|
public interface ITimeSeriesClient<TDto>
|
||||||
|
where TDto : class, new()
|
||||||
|
{
|
||||||
|
private const string BaseRoute = "/api/dataSaub";
|
||||||
|
|
||||||
|
[Post($"{BaseRoute}")]
|
||||||
|
Task<IApiResponse<int>> InsertRange(IEnumerable<TDto> dtos);
|
||||||
|
|
||||||
|
[Get($"{BaseRoute}")]
|
||||||
|
Task<IApiResponse<IEnumerable<TDto>>> Get(DateTimeOffset dateBegin, DateTimeOffset dateEnd);
|
||||||
|
|
||||||
|
[Get($"{BaseRoute}/resampled")]
|
||||||
|
Task<IApiResponse<IEnumerable<TDto>>> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024);
|
||||||
|
|
||||||
|
[Get($"{BaseRoute}/datesRange")]
|
||||||
|
Task<IApiResponse<DatesRangeDto?>> GetDatesRange();
|
||||||
|
}
|
62
Persistence.Client/Clients/ITimestampedSetClient.cs
Normal file
62
Persistence.Client/Clients/ITimestampedSetClient.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using Persistence.Models;
|
||||||
|
using Refit;
|
||||||
|
|
||||||
|
namespace Persistence.Client.Clients;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Клиент для работы с репозиторием для хранения разных наборов данных рядов.
|
||||||
|
/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest.
|
||||||
|
/// idDiscriminator формируют клиенты и только им известно что они обозначают.
|
||||||
|
/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITimestampedSetClient
|
||||||
|
{
|
||||||
|
private const string baseUrl = "/api/TimestampedSet/{idDiscriminator}";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавление новых данных
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="sets"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Post(baseUrl)]
|
||||||
|
Task<IApiResponse<int>> InsertRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных с фильтрацией. Значение фильтра null - отключен
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="geTimestamp">Фильтр позднее даты</param>
|
||||||
|
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||||
|
/// <param name="skip"></param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Get(baseUrl)]
|
||||||
|
Task<IApiResponse<IEnumerable<TimestampedSetDto>>> Get(Guid idDiscriminator, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable<string>? columnNames, int skip, int take);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить последние данные
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Get($"{baseUrl}/last")]
|
||||||
|
Task<IApiResponse<IEnumerable<TimestampedSetDto>>> GetLast(Guid idDiscriminator, [Query] IEnumerable<string>? columnNames, int take);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Количество записей по указанному набору в БД. Для пагинации.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Get($"{baseUrl}/count")]
|
||||||
|
Task<IApiResponse<int>> Count(Guid idDiscriminator);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Диапазон дат за которые есть данные
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Get($"{baseUrl}/datesRange")]
|
||||||
|
Task<IApiResponse<DatesRangeDto?>> GetDatesRange(Guid idDiscriminator);
|
||||||
|
}
|
72
Persistence.Client/Helpers/ApiTokenHelper.cs
Normal file
72
Persistence.Client/Helpers/ApiTokenHelper.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Persistence.Models.Configurations;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace Persistence.Client.Helpers;
|
||||||
|
public static class ApiTokenHelper
|
||||||
|
{
|
||||||
|
public static void Authorize(this HttpClient httpClient, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var authUser = configuration
|
||||||
|
.GetSection(nameof(AuthUser))
|
||||||
|
.Get<AuthUser>()!;
|
||||||
|
var needUseKeyCloak = configuration
|
||||||
|
.GetSection("NeedUseKeyCloak")
|
||||||
|
.Get<bool>()!;
|
||||||
|
var keycloakGetTokenUrl = configuration.GetSection("KeycloakGetTokenUrl").Get<string>() ?? string.Empty;
|
||||||
|
|
||||||
|
var jwtToken = needUseKeyCloak
|
||||||
|
? authUser.CreateKeyCloakJwtToken(keycloakGetTokenUrl)
|
||||||
|
: authUser.CreateDefaultJwtToken();
|
||||||
|
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateDefaultJwtToken(this AuthUser authUser)
|
||||||
|
{
|
||||||
|
var claims = new List<Claim>()
|
||||||
|
{
|
||||||
|
new("client_id", authUser.ClientId),
|
||||||
|
new("username", authUser.Username),
|
||||||
|
new("password", authUser.Password),
|
||||||
|
new("grant_type", authUser.GrantType)
|
||||||
|
};
|
||||||
|
|
||||||
|
var tokenDescriptor = new SecurityTokenDescriptor
|
||||||
|
{
|
||||||
|
Issuer = JwtParams.Issuer,
|
||||||
|
Audience = JwtParams.Audience,
|
||||||
|
Subject = new ClaimsIdentity(claims),
|
||||||
|
Expires = DateTime.UtcNow.AddHours(1),
|
||||||
|
SigningCredentials = new SigningCredentials(JwtParams.SecurityKey, SecurityAlgorithms.HmacSha256Signature)
|
||||||
|
};
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||||
|
return tokenHandler.WriteToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateKeyCloakJwtToken(this AuthUser authUser, string keycloakGetTokenUrl)
|
||||||
|
{
|
||||||
|
var restClient = new RestClient();
|
||||||
|
|
||||||
|
var request = new RestRequest(keycloakGetTokenUrl, Method.Post);
|
||||||
|
request.AddParameter("username", authUser.Username);
|
||||||
|
request.AddParameter("password", authUser.Password);
|
||||||
|
request.AddParameter("client_id", authUser.ClientId);
|
||||||
|
request.AddParameter("grant_type", authUser.GrantType);
|
||||||
|
|
||||||
|
var keyCloackResponse = restClient.Post(request);
|
||||||
|
if (keyCloackResponse.IsSuccessful && !String.IsNullOrEmpty(keyCloackResponse.Content))
|
||||||
|
{
|
||||||
|
var token = JsonSerializer.Deserialize<JwtToken>(keyCloackResponse.Content)!;
|
||||||
|
return token.AccessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.Empty;
|
||||||
|
}
|
||||||
|
}
|
25
Persistence.Client/Persistence.Client.csproj
Normal file
25
Persistence.Client/Persistence.Client.csproj
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.2.1" />
|
||||||
|
<PackageReference Include="Refit" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
|
||||||
|
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Persistence\Persistence.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
32
Persistence.Client/PersistenceClientFactory.cs
Normal file
32
Persistence.Client/PersistenceClientFactory.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Persistence.Client.Helpers;
|
||||||
|
using Refit;
|
||||||
|
|
||||||
|
namespace Persistence.Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Фабрика клиентов для доступа к Persistence - сервису
|
||||||
|
/// </summary>
|
||||||
|
public class PersistenceClientFactory
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions));
|
||||||
|
private HttpClient httpClient;
|
||||||
|
public PersistenceClientFactory(IHttpClientFactory httpClientFactory, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
this.httpClient = httpClientFactory.CreateClient();
|
||||||
|
|
||||||
|
httpClient.Authorize(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetClient<T>()
|
||||||
|
{
|
||||||
|
return RestService.For<T>(httpClient, RefitSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Persistence.Database.Postgres/DependencyInjection.cs
Normal file
20
Persistence.Database.Postgres/DependencyInjection.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Persistence.Database.Model;
|
||||||
|
|
||||||
|
public static class DependencyInjection
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddPersistenceDbContext(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
string connectionStringName = "DefaultConnection";
|
||||||
|
|
||||||
|
services.AddDbContext<PersistenceDbContext>(options =>
|
||||||
|
options.UseNpgsql(configuration.GetConnectionString(connectionStringName)));
|
||||||
|
|
||||||
|
services.AddScoped<DbContext>(provider => provider.GetRequiredService<PersistenceDbContext>());
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Persistence.Database;
|
namespace Persistence.Database.Postgres;
|
||||||
public static class EFExtensionsInitialization
|
public static class EFExtensionsInitialization
|
||||||
{
|
{
|
||||||
public static void EnsureCreatedAndMigrated(this DatabaseFacade db)
|
public static void EnsureCreatedAndMigrated(this DatabaseFacade db)
|
146
Persistence.Database.Postgres/Migrations/20241118052225_SetpointMigration.Designer.cs
generated
Normal file
146
Persistence.Database.Postgres/Migrations/20241118052225_SetpointMigration.Designer.cs
generated
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PersistenceDbContext))]
|
||||||
|
[Migration("20241118052225_SetpointMigration")]
|
||||||
|
partial class SetpointMigration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.UseCollation("Russian_Russia.1251")
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.10")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<double?>("AxialLoad")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("axialLoad");
|
||||||
|
|
||||||
|
b.Property<double?>("BitDepth")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("bitDepth");
|
||||||
|
|
||||||
|
b.Property<double?>("BlockPosition")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("blockPosition");
|
||||||
|
|
||||||
|
b.Property<double?>("BlockSpeed")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("blockSpeed");
|
||||||
|
|
||||||
|
b.Property<double?>("Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("flow");
|
||||||
|
|
||||||
|
b.Property<double?>("HookWeight")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("hookWeight");
|
||||||
|
|
||||||
|
b.Property<int>("IdFeedRegulator")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("idFeedRegulator");
|
||||||
|
|
||||||
|
b.Property<int?>("Mode")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("mode");
|
||||||
|
|
||||||
|
b.Property<double?>("Mse")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("mse");
|
||||||
|
|
||||||
|
b.Property<short>("MseState")
|
||||||
|
.HasColumnType("smallint")
|
||||||
|
.HasColumnName("mseState");
|
||||||
|
|
||||||
|
b.Property<double?>("Pressure")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pressure");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump0Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump0Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump1Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump1Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump2Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump2Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("RotorSpeed")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("rotorSpeed");
|
||||||
|
|
||||||
|
b.Property<double?>("RotorTorque")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("rotorTorque");
|
||||||
|
|
||||||
|
b.Property<int>("TimeStamp")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("timestamp");
|
||||||
|
|
||||||
|
b.Property<string>("User")
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("user");
|
||||||
|
|
||||||
|
b.Property<double?>("WellDepth")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("wellDepth");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("DataSaub");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Persistence.Database.Model.Setpoint", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Key")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Ключ");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Created")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("Дата изменения уставки");
|
||||||
|
|
||||||
|
b.Property<int>("IdUser")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasComment("Id автора последнего изменения");
|
||||||
|
|
||||||
|
b.Property<object>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasComment("Значение уставки");
|
||||||
|
|
||||||
|
b.HasKey("Key", "Created");
|
||||||
|
|
||||||
|
b.ToTable("Setpoint");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class SetpointMigration : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Setpoint",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Key = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ"),
|
||||||
|
Created = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата изменения уставки"),
|
||||||
|
Value = table.Column<object>(type: "jsonb", nullable: false, comment: "Значение уставки"),
|
||||||
|
IdUser = table.Column<int>(type: "integer", nullable: false, comment: "Id автора последнего изменения")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Created });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Setpoint");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
115
Persistence.Database.Postgres/Migrations/20241122074646_InitialCreate.Designer.cs
generated
Normal file
115
Persistence.Database.Postgres/Migrations/20241122074646_InitialCreate.Designer.cs
generated
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PersistenceDbContext))]
|
||||||
|
[Migration("20241122074646_InitialCreate")]
|
||||||
|
partial class InitialCreate
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.UseCollation("Russian_Russia.1251")
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.10")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
||||||
|
{
|
||||||
|
b.Property<DateTimeOffset>("Date")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("date");
|
||||||
|
|
||||||
|
b.Property<double?>("AxialLoad")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("axialLoad");
|
||||||
|
|
||||||
|
b.Property<double?>("BitDepth")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("bitDepth");
|
||||||
|
|
||||||
|
b.Property<double?>("BlockPosition")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("blockPosition");
|
||||||
|
|
||||||
|
b.Property<double?>("BlockSpeed")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("blockSpeed");
|
||||||
|
|
||||||
|
b.Property<double?>("Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("flow");
|
||||||
|
|
||||||
|
b.Property<double?>("HookWeight")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("hookWeight");
|
||||||
|
|
||||||
|
b.Property<int>("IdFeedRegulator")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("idFeedRegulator");
|
||||||
|
|
||||||
|
b.Property<int?>("Mode")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("mode");
|
||||||
|
|
||||||
|
b.Property<double?>("Mse")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("mse");
|
||||||
|
|
||||||
|
b.Property<short>("MseState")
|
||||||
|
.HasColumnType("smallint")
|
||||||
|
.HasColumnName("mseState");
|
||||||
|
|
||||||
|
b.Property<double?>("Pressure")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pressure");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump0Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump0Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump1Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump1Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump2Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump2Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("RotorSpeed")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("rotorSpeed");
|
||||||
|
|
||||||
|
b.Property<double?>("RotorTorque")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("rotorTorque");
|
||||||
|
|
||||||
|
b.Property<string>("User")
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("user");
|
||||||
|
|
||||||
|
b.Property<double?>("WellDepth")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("wellDepth");
|
||||||
|
|
||||||
|
b.HasKey("Date");
|
||||||
|
|
||||||
|
b.ToTable("DataSaub");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class InitialCreate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterDatabase()
|
||||||
|
.Annotation("Npgsql:PostgresExtension:adminpack", ",,");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "DataSaub",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
date = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
mode = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
user = table.Column<string>(type: "text", nullable: true),
|
||||||
|
wellDepth = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
bitDepth = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
blockPosition = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
blockSpeed = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
pressure = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
axialLoad = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
hookWeight = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
rotorTorque = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
rotorSpeed = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
flow = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
mseState = table.Column<short>(type: "smallint", nullable: false),
|
||||||
|
idFeedRegulator = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
mse = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
pump0Flow = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
pump1Flow = table.Column<double>(type: "double precision", nullable: true),
|
||||||
|
pump2Flow = table.Column<double>(type: "double precision", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_DataSaub", x => x.date);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "DataSaub");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PersistenceDbContext))]
|
||||||
|
partial class PersistenceDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.UseCollation("Russian_Russia.1251")
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.10")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
||||||
|
{
|
||||||
|
b.Property<DateTimeOffset>("Date")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("date");
|
||||||
|
|
||||||
|
b.Property<double?>("AxialLoad")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("axialLoad");
|
||||||
|
|
||||||
|
b.Property<double?>("BitDepth")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("bitDepth");
|
||||||
|
|
||||||
|
b.Property<double?>("BlockPosition")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("blockPosition");
|
||||||
|
|
||||||
|
b.Property<double?>("BlockSpeed")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("blockSpeed");
|
||||||
|
|
||||||
|
b.Property<double?>("Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("flow");
|
||||||
|
|
||||||
|
b.Property<double?>("HookWeight")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("hookWeight");
|
||||||
|
|
||||||
|
b.Property<int>("IdFeedRegulator")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("idFeedRegulator");
|
||||||
|
|
||||||
|
b.Property<int?>("Mode")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("mode");
|
||||||
|
|
||||||
|
b.Property<double?>("Mse")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("mse");
|
||||||
|
|
||||||
|
b.Property<short>("MseState")
|
||||||
|
.HasColumnType("smallint")
|
||||||
|
.HasColumnName("mseState");
|
||||||
|
|
||||||
|
b.Property<double?>("Pressure")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pressure");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump0Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump0Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump1Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump1Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("Pump2Flow")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("pump2Flow");
|
||||||
|
|
||||||
|
b.Property<double?>("RotorSpeed")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("rotorSpeed");
|
||||||
|
|
||||||
|
b.Property<double?>("RotorTorque")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("rotorTorque");
|
||||||
|
|
||||||
|
b.Property<string>("User")
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("user");
|
||||||
|
|
||||||
|
b.Property<double?>("WellDepth")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("wellDepth");
|
||||||
|
|
||||||
|
b.HasKey("Date");
|
||||||
|
|
||||||
|
b.ToTable("DataSaub");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Persistence.Database.Model.Setpoint", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Key")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Ключ");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Created")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("Дата изменения уставки");
|
||||||
|
|
||||||
|
b.Property<int>("IdUser")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasComment("Id автора последнего изменения");
|
||||||
|
|
||||||
|
b.Property<object>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasComment("Значение уставки");
|
||||||
|
|
||||||
|
b.HasKey("Key", "Created");
|
||||||
|
|
||||||
|
b.ToTable("Setpoint");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Persistence.Database\Persistence.Database.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Migrations\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -1,22 +1,27 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using Npgsql;
|
||||||
using System.Collections.Generic;
|
using Persistence.Database.Entity;
|
||||||
using System.Diagnostics.Metrics;
|
using System.Data.Common;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
|
|
||||||
|
|
||||||
namespace Persistence.Database.Model;
|
namespace Persistence.Database.Model;
|
||||||
public partial class PersistenceDbContext : DbContext, IPersistenceDbContext
|
public partial class PersistenceDbContext : DbContext
|
||||||
{
|
{
|
||||||
public DbSet<DataSaub> DataSaub => Set<DataSaub>();
|
public DbSet<DataSaub> DataSaub => Set<DataSaub>();
|
||||||
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
|
|
||||||
|
|
||||||
public PersistenceDbContext() { } // Áåç ïóñòîãî êîíñòðóêòîðà ìèãðàöèÿ ïàäààåò
|
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
||||||
|
|
||||||
public PersistenceDbContext(DbContextOptions<PersistenceDbContext> options)
|
public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>();
|
||||||
|
|
||||||
|
public PersistenceDbContext()
|
||||||
|
: base()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public PersistenceDbContext(DbContextOptions<PersistenceDbContext> options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
@ -31,7 +36,9 @@ public partial class PersistenceDbContext : DbContext, IPersistenceDbContext
|
|||||||
{
|
{
|
||||||
modelBuilder.HasPostgresExtension("adminpack")
|
modelBuilder.HasPostgresExtension("adminpack")
|
||||||
.HasAnnotation("Relational:Collation", "Russian_Russia.1251");
|
.HasAnnotation("Relational:Collation", "Russian_Russia.1251");
|
||||||
|
|
||||||
|
modelBuilder.Entity<TimestampedSet>()
|
||||||
|
.Property(e => e.Set)
|
||||||
|
.HasJsonConversion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
5
Persistence.Database.Postgres/Readme.md
Normal file
5
Persistence.Database.Postgres/Readme.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
## Создать миграцию
|
||||||
|
```
|
||||||
|
dotnet ef migrations add <MigrationName> --project Persistence.Database.Postgres
|
||||||
|
|
||||||
|
```
|
41
Persistence.Database/EFExtensions.cs
Normal file
41
Persistence.Database/EFExtensions.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Persistence.Database;
|
||||||
|
|
||||||
|
public static class EFExtensions
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions jsonSerializerOptions = new()
|
||||||
|
{
|
||||||
|
AllowTrailingCommas = true,
|
||||||
|
WriteIndented = true,
|
||||||
|
NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.AllowNamedFloatingPointLiterals,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> HasJsonConversion<TProperty>(
|
||||||
|
this Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> builder)
|
||||||
|
=> HasJsonConversion(builder, jsonSerializerOptions);
|
||||||
|
|
||||||
|
public static Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> HasJsonConversion<TProperty>(
|
||||||
|
this Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> builder,
|
||||||
|
JsonSerializerOptions jsonSerializerOptions)
|
||||||
|
{
|
||||||
|
builder.HasConversion(
|
||||||
|
s => JsonSerializer.Serialize(s, jsonSerializerOptions),
|
||||||
|
s => JsonSerializer.Deserialize<TProperty>(s, jsonSerializerOptions)!);
|
||||||
|
|
||||||
|
ValueComparer<TProperty> valueComparer = new(
|
||||||
|
(a, b) =>
|
||||||
|
(a != null) && (b != null)
|
||||||
|
? a.GetHashCode() == b.GetHashCode()
|
||||||
|
: (a == null) && (b == null),
|
||||||
|
i => (i == null) ? -1 : i.GetHashCode(),
|
||||||
|
i => i);
|
||||||
|
|
||||||
|
builder.Metadata.SetValueComparer(valueComparer);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,12 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace Persistence.Database.Model;
|
namespace Persistence.Database.Model;
|
||||||
public class DataSaub : ITimestampedData
|
public class DataSaub : ITimestampedData
|
||||||
{
|
{
|
||||||
[Column("id")]
|
[Key, Column("date")]
|
||||||
public int Id { get; set; }
|
public DateTimeOffset Date { get; set; }
|
||||||
|
|
||||||
[Column("timestamp")]
|
|
||||||
public int TimeStamp { get; set; }
|
|
||||||
|
|
||||||
[Column("mode")]
|
[Column("mode")]
|
||||||
public int? Mode { get; set; }
|
public int? Mode { get; set; }
|
@ -7,5 +7,8 @@ using System.Threading.Tasks;
|
|||||||
namespace Persistence.Database.Model;
|
namespace Persistence.Database.Model;
|
||||||
public interface ITimestampedData
|
public interface ITimestampedData
|
||||||
{
|
{
|
||||||
int TimeStamp { get; set; }
|
/// <summary>
|
||||||
|
/// Дата (должна быть обязательно в UTC)
|
||||||
|
/// </summary>
|
||||||
|
DateTimeOffset Date { get; set; }
|
||||||
}
|
}
|
21
Persistence.Database/Entity/Setpoint.cs
Normal file
21
Persistence.Database/Entity/Setpoint.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Persistence.Database.Model
|
||||||
|
{
|
||||||
|
[PrimaryKey(nameof(Key), nameof(Created))]
|
||||||
|
public class Setpoint
|
||||||
|
{
|
||||||
|
[Comment("Ключ")]
|
||||||
|
public Guid Key { get; set; }
|
||||||
|
|
||||||
|
[Column(TypeName = "jsonb"), Comment("Значение уставки")]
|
||||||
|
public required object Value { get; set; }
|
||||||
|
|
||||||
|
[Comment("Дата создания уставки")]
|
||||||
|
public DateTimeOffset Created { get; set; }
|
||||||
|
|
||||||
|
[Comment("Id автора последнего изменения")]
|
||||||
|
public int IdUser { get; set; }
|
||||||
|
}
|
||||||
|
}
|
11
Persistence.Database/Entity/TimestampedSet.cs
Normal file
11
Persistence.Database/Entity/TimestampedSet.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Persistence.Database.Entity;
|
||||||
|
|
||||||
|
[Comment("Общая таблица данных временных рядов")]
|
||||||
|
[PrimaryKey(nameof(IdDiscriminator), nameof(Timestamp))]
|
||||||
|
public record TimestampedSet(
|
||||||
|
[property: Comment("Дискриминатор ссылка на тип сохраняемых данных")] Guid IdDiscriminator,
|
||||||
|
[property: Comment("Отметка времени, строго в UTC")] DateTimeOffset Timestamp,
|
||||||
|
[property: Column(TypeName = "jsonb"), Comment("Набор сохраняемых данных")] IDictionary<string, object> Set);
|
@ -1,22 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace Persistence.Database.Model
|
|
||||||
{
|
|
||||||
public class ChangeLog : IChangeLogData
|
|
||||||
{
|
|
||||||
[Column("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
[Column("idnext")]
|
|
||||||
public int? IdNext { get; set; }
|
|
||||||
|
|
||||||
[Column("idprevious")]
|
|
||||||
public int? IdPrevious { get; set; }
|
|
||||||
|
|
||||||
[Column("creation")]
|
|
||||||
public DateTimeOffset Creation { get; set; }
|
|
||||||
|
|
||||||
[Column("obsolete")]
|
|
||||||
public DateTimeOffset? Obsolete { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
namespace Persistence.Database.Model
|
|
||||||
{
|
|
||||||
internal interface IChangeLogData
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public int? IdNext { get; set; }
|
|
||||||
public int? IdPrevious { get; set; }
|
|
||||||
public DateTimeOffset Creation { get; set; }
|
|
||||||
public DateTimeOffset? Obsolete { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data.Common;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.Database.Model;
|
|
||||||
public interface IDbContextManager
|
|
||||||
{
|
|
||||||
//IConnectionManager ConnectionManager { get; }
|
|
||||||
|
|
||||||
DbContext GetReadonlyDbContext();
|
|
||||||
|
|
||||||
DbContext GetDbContext();
|
|
||||||
|
|
||||||
DbContext CreateAndInitializeNewContext();
|
|
||||||
|
|
||||||
DbContext CreateAndInitializeNewContext(DbConnection connection);
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.Metrics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.Database.Model;
|
|
||||||
public interface IPersistenceDbContext : IDisposable
|
|
||||||
{
|
|
||||||
DbSet<DataSaub> DataSaub { get; }
|
|
||||||
DbSet<ChangeLog> ChangeLog { get; }
|
|
||||||
DatabaseFacade Database { get; }
|
|
||||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
|
|
||||||
}
|
|
@ -7,11 +7,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
namespace Persistence.IntegrationTests;
|
|
||||||
public static class ApiTokenHelper
|
|
||||||
{
|
|
||||||
//public static string GetAdminUserToken()
|
|
||||||
//{
|
|
||||||
// var user = new User()
|
|
||||||
// {
|
|
||||||
// Id = 1,
|
|
||||||
// IdCompany = 1,
|
|
||||||
// Login = "test_user"
|
|
||||||
// };
|
|
||||||
// var roles = new[] { "root" };
|
|
||||||
|
|
||||||
// return CreateToken(user, roles);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private static string CreateToken(User user, IEnumerable<string> roles)
|
|
||||||
//{
|
|
||||||
// var claims = new List<Claim>
|
|
||||||
// {
|
|
||||||
// new("id", user.Id.ToString()),
|
|
||||||
// new(ClaimsIdentity.DefaultNameClaimType, user.Login),
|
|
||||||
// new("idCompany", user.IdCompany.ToString()),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
|
|
||||||
|
|
||||||
// const string secret = "супер секретный ключ для шифрования";
|
|
||||||
|
|
||||||
// var key = Encoding.ASCII.GetBytes(secret);
|
|
||||||
// var tokenDescriptor = new SecurityTokenDescriptor
|
|
||||||
// {
|
|
||||||
// Issuer = "a",
|
|
||||||
// Audience = "a",
|
|
||||||
// Subject = new ClaimsIdentity(claims),
|
|
||||||
// Expires = DateTime.UtcNow.AddHours(1),
|
|
||||||
// SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
|
||||||
// };
|
|
||||||
// var tokenHandler = new JwtSecurityTokenHandler();
|
|
||||||
// var token = tokenHandler.CreateToken(tokenDescriptor);
|
|
||||||
// return tokenHandler.WriteToken(token);
|
|
||||||
//}
|
|
||||||
}
|
|
@ -17,9 +17,9 @@ public abstract class BaseIntegrationTest : IClassFixture<WebAppFactoryFixture>,
|
|||||||
|
|
||||||
protected BaseIntegrationTest(WebAppFactoryFixture factory)
|
protected BaseIntegrationTest(WebAppFactoryFixture factory)
|
||||||
{
|
{
|
||||||
//scope = factory.Services.CreateScope();
|
scope = factory.Services.CreateScope();
|
||||||
|
|
||||||
//dbContext = scope.ServiceProvider.GetRequiredService<PersistenceDbContext>();
|
dbContext = scope.ServiceProvider.GetRequiredService<PersistenceDbContext>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Persistence.Repository.Data;
|
|
||||||
using Refit;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.IntegrationTests.Clients;
|
|
||||||
public interface ITimeSeriesClient<TDto>
|
|
||||||
where TDto : class, new()
|
|
||||||
{
|
|
||||||
[Post("/api/dataSaub")]
|
|
||||||
Task<IApiResponse<int>> InsertRangeAsync(IEnumerable<TDto> dtos);
|
|
||||||
}
|
|
@ -1,36 +1,55 @@
|
|||||||
using Persistence.Repository.Data;
|
using Persistence.Client;
|
||||||
using System;
|
using Persistence.Database.Model;
|
||||||
using System.Collections.Generic;
|
using Persistence.Repository.Data;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Persistence.IntegrationTests.Controllers;
|
namespace Persistence.IntegrationTests.Controllers;
|
||||||
public class DataSaubControllerTest : TimeSeriesBaseControllerTest<DataSaubDto>
|
public class DataSaubControllerTest : TimeSeriesBaseControllerTest<DataSaub, DataSaubDto>
|
||||||
{
|
{
|
||||||
private readonly DataSaubDto dto = new DataSaubDto()
|
private readonly DataSaubDto dto = new DataSaubDto()
|
||||||
{
|
{
|
||||||
AxialLoad = 1,
|
AxialLoad = 1,
|
||||||
BitDepth = 2,
|
BitDepth = 2,
|
||||||
BlockPosition = 3,
|
BlockPosition = 3,
|
||||||
BlockSpeed = 4,
|
BlockSpeed = 4,
|
||||||
Date = DateTimeOffset.Now,
|
Date = DateTimeOffset.UtcNow,
|
||||||
Flow = 5,
|
Flow = 5,
|
||||||
HookWeight = 6,
|
HookWeight = 6,
|
||||||
Id = 7,
|
IdFeedRegulator = 8,
|
||||||
IdFeedRegulator = 8,
|
Mode = 9,
|
||||||
Mode = 9,
|
Mse = 10,
|
||||||
Mse = 10,
|
MseState = 11,
|
||||||
MseState = 11,
|
Pressure = 12,
|
||||||
Pressure = 12,
|
Pump0Flow = 13,
|
||||||
Pump0Flow = 13,
|
Pump1Flow = 14,
|
||||||
Pump1Flow = 14,
|
Pump2Flow = 15,
|
||||||
Pump2Flow = 15,
|
RotorSpeed = 16,
|
||||||
RotorSpeed = 16,
|
RotorTorque = 17,
|
||||||
RotorTorque = 17,
|
User = string.Empty,
|
||||||
User = string.Empty,
|
WellDepth = 18,
|
||||||
WellDepth = 18,
|
};
|
||||||
|
|
||||||
|
private readonly DataSaub entity = new DataSaub()
|
||||||
|
{
|
||||||
|
AxialLoad = 1,
|
||||||
|
BitDepth = 2,
|
||||||
|
BlockPosition = 3,
|
||||||
|
BlockSpeed = 4,
|
||||||
|
Date = DateTimeOffset.UtcNow,
|
||||||
|
Flow = 5,
|
||||||
|
HookWeight = 6,
|
||||||
|
IdFeedRegulator = 8,
|
||||||
|
Mode = 9,
|
||||||
|
Mse = 10,
|
||||||
|
MseState = 11,
|
||||||
|
Pressure = 12,
|
||||||
|
Pump0Flow = 13,
|
||||||
|
Pump1Flow = 14,
|
||||||
|
Pump2Flow = 15,
|
||||||
|
RotorSpeed = 16,
|
||||||
|
RotorTorque = 17,
|
||||||
|
User = string.Empty,
|
||||||
|
WellDepth = 18,
|
||||||
};
|
};
|
||||||
|
|
||||||
public DataSaubControllerTest(WebAppFactoryFixture factory) : base(factory)
|
public DataSaubControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||||
@ -42,4 +61,26 @@ public class DataSaubControllerTest : TimeSeriesBaseControllerTest<DataSaubDto>
|
|||||||
{
|
{
|
||||||
await InsertRangeSuccess(dto);
|
await InsertRangeSuccess(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_returns_success()
|
||||||
|
{
|
||||||
|
var beginDate = DateTimeOffset.UtcNow.AddDays(-1);
|
||||||
|
var endDate = DateTimeOffset.UtcNow;
|
||||||
|
await GetSuccess(beginDate, endDate, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetDatesRange_returns_success()
|
||||||
|
{
|
||||||
|
await GetDatesRangeSuccess(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetResampledData_returns_success()
|
||||||
|
{
|
||||||
|
await GetResampledDataSuccess(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,159 @@
|
|||||||
|
using System.Net;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Persistence.Client;
|
||||||
|
using Persistence.Client.Clients;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Persistence.IntegrationTests.Controllers
|
||||||
|
{
|
||||||
|
public class SetpointControllerTest : BaseIntegrationTest
|
||||||
|
{
|
||||||
|
private ISetpointClient setpointClient;
|
||||||
|
private class TestObject
|
||||||
|
{
|
||||||
|
public string? value1 { get; set; }
|
||||||
|
public int? value2 { get; set; }
|
||||||
|
}
|
||||||
|
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||||
|
{
|
||||||
|
var scope = factory.Services.CreateScope();
|
||||||
|
var persistenceClientFactory = scope.ServiceProvider
|
||||||
|
.GetRequiredService<PersistenceClientFactory>();
|
||||||
|
|
||||||
|
setpointClient = persistenceClientFactory.GetClient<ISetpointClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetCurrent_returns_success()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var setpointKeys = new List<Guid>()
|
||||||
|
{
|
||||||
|
Guid.NewGuid(),
|
||||||
|
Guid.NewGuid()
|
||||||
|
};
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await setpointClient.GetCurrent(setpointKeys);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.Empty(response.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetCurrent_AfterSave_returns_success()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var setpointKey = await Save();
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await setpointClient.GetCurrent([setpointKey]);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.NotEmpty(response.Content);
|
||||||
|
Assert.Equal(setpointKey, response.Content.FirstOrDefault()?.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetHistory_returns_success()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var setpointKeys = new List<Guid>()
|
||||||
|
{
|
||||||
|
Guid.NewGuid(),
|
||||||
|
Guid.NewGuid()
|
||||||
|
};
|
||||||
|
var historyMoment = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await setpointClient.GetHistory(setpointKeys, historyMoment);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.Empty(response.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetHistory_AfterSave_returns_success()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var setpointKey = await Save();
|
||||||
|
var historyMoment = DateTimeOffset.UtcNow;
|
||||||
|
historyMoment = historyMoment.AddDays(1);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await setpointClient.GetHistory([setpointKey], historyMoment);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.NotEmpty(response.Content);
|
||||||
|
Assert.Equal(setpointKey, response.Content.FirstOrDefault()?.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetLog_returns_success()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var setpointKeys = new List<Guid>()
|
||||||
|
{
|
||||||
|
Guid.NewGuid(),
|
||||||
|
Guid.NewGuid()
|
||||||
|
};
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await setpointClient.GetLog(setpointKeys);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.Empty(response.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetLog_AfterSave_returns_success()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var setpointKey = await Save();
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await setpointClient.GetLog([setpointKey]);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.NotEmpty(response.Content);
|
||||||
|
Assert.Equal(setpointKey, response.Content.FirstOrDefault().Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Save_returns_success()
|
||||||
|
{
|
||||||
|
await Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Guid> Save()
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var setpointKey = Guid.NewGuid();
|
||||||
|
var setpointValue = new TestObject()
|
||||||
|
{
|
||||||
|
value1 = "1",
|
||||||
|
value2 = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await setpointClient.Save(setpointKey, setpointValue);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
return setpointKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +1,27 @@
|
|||||||
using Mapster;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Persistence.IntegrationTests.Clients;
|
|
||||||
using Persistence.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using Mapster;
|
||||||
using System.Threading.Tasks;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Persistence.Client;
|
||||||
|
using Persistence.Client.Clients;
|
||||||
|
using Persistence.Database.Model;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Persistence.IntegrationTests.Controllers;
|
namespace Persistence.IntegrationTests.Controllers;
|
||||||
public abstract class TimeSeriesBaseControllerTest<TDto> : BaseIntegrationTest
|
public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrationTest
|
||||||
|
where TEntity : class, ITimestampedData, new()
|
||||||
where TDto : class, new()
|
where TDto : class, new()
|
||||||
{
|
{
|
||||||
private ITimeSeriesClient<TDto> client;
|
private ITimeSeriesClient<TDto> timeSeriesClient;
|
||||||
|
|
||||||
public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory)
|
public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||||
{
|
{
|
||||||
//dbContext.CleanupDbSet<TEntity>();
|
dbContext.CleanupDbSet<TEntity>();
|
||||||
client = factory.GetHttpClient<ITimeSeriesClient<TDto>>(string.Empty);
|
|
||||||
|
var scope = factory.Services.CreateScope();
|
||||||
|
var persistenceClientFactory = scope.ServiceProvider
|
||||||
|
.GetRequiredService<PersistenceClientFactory>();
|
||||||
|
|
||||||
|
timeSeriesClient = persistenceClientFactory.GetClient<ITimeSeriesClient<TDto>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InsertRangeSuccess(TDto dto)
|
public async Task InsertRangeSuccess(TDto dto)
|
||||||
@ -29,23 +30,96 @@ public abstract class TimeSeriesBaseControllerTest<TDto> : BaseIntegrationTest
|
|||||||
var expected = dto.Adapt<TDto>();
|
var expected = dto.Adapt<TDto>();
|
||||||
|
|
||||||
//act
|
//act
|
||||||
var response = await client.InsertRangeAsync(new TDto[] { expected });
|
var response = await timeSeriesClient.InsertRange(new TDto[] { expected });
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
Assert.Equal(1, response.Content);
|
Assert.Equal(1, response.Content);
|
||||||
|
|
||||||
//var entity = GetByWellId();
|
|
||||||
|
|
||||||
//Assert.NotNull(entity);
|
|
||||||
|
|
||||||
//var actual = entity.Adapt<ChangeLogDto<TDto>>();
|
|
||||||
//Assert.Equal(ProcessMapPlanBase.IdStateActual, actual.IdState);
|
|
||||||
|
|
||||||
//var excludeProps = new[] {
|
|
||||||
// nameof(ProcessMapPlanBaseDto.Id),
|
|
||||||
// nameof(ProcessMapPlanBaseDto.Section)
|
|
||||||
//};
|
|
||||||
//MatchHelper.Match(expected, actual.Item, excludeProps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task GetSuccess(DateTimeOffset beginDate, DateTimeOffset endDate, TEntity entity)
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var dbset = dbContext.Set<TEntity>();
|
||||||
|
|
||||||
|
dbset.Add(entity);
|
||||||
|
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
var response = await timeSeriesClient.Get(beginDate, endDate);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.Single(response.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GetDatesRangeSuccess(TEntity entity)
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var datesRangeExpected = 30;
|
||||||
|
|
||||||
|
var entity2 = entity.Adapt<TEntity>();
|
||||||
|
entity2.Date = entity.Date.AddDays(datesRangeExpected);
|
||||||
|
|
||||||
|
var dbset = dbContext.Set<TEntity>();
|
||||||
|
dbset.Add(entity);
|
||||||
|
dbset.Add(entity2);
|
||||||
|
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
var response = await timeSeriesClient.GetDatesRange();
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
|
||||||
|
var datesRangeActual = (response.Content.To - response.Content.From).Days;
|
||||||
|
Assert.Equal(datesRangeExpected, datesRangeActual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GetResampledDataSuccess(TEntity entity)
|
||||||
|
{
|
||||||
|
//arrange
|
||||||
|
var approxPointsCount = 10;
|
||||||
|
var differenceBetweenStartAndEndDays = 50;
|
||||||
|
|
||||||
|
var entities = new List<TEntity>();
|
||||||
|
for (var i = 1; i <= differenceBetweenStartAndEndDays; i++)
|
||||||
|
{
|
||||||
|
var entity2 = entity.Adapt<TEntity>();
|
||||||
|
entity2.Date = entity.Date.AddDays(i - 1);
|
||||||
|
|
||||||
|
entities.Add(entity2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbset = dbContext.Set<TEntity>();
|
||||||
|
dbset.AddRange(entities);
|
||||||
|
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
var response = await timeSeriesClient.GetResampledData(entity.Date.AddMinutes(-1), differenceBetweenStartAndEndDays * 24 * 60 * 60 + 60, approxPointsCount);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
|
||||||
|
var ratio = entities.Count() / approxPointsCount;
|
||||||
|
if (ratio > 1)
|
||||||
|
{
|
||||||
|
var expectedResampledCount = entities
|
||||||
|
.Where((_, index) => index % ratio == 0)
|
||||||
|
.Count();
|
||||||
|
|
||||||
|
Assert.Equal(expectedResampledCount, response.Content.Count());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Equal(entities.Count(), response.Content.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,222 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Persistence.Client;
|
||||||
|
using Persistence.Client.Clients;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Persistence.IntegrationTests.Controllers;
|
||||||
|
public class TimestampedSetControllerTest : BaseIntegrationTest
|
||||||
|
{
|
||||||
|
private readonly ITimestampedSetClient client;
|
||||||
|
|
||||||
|
public TimestampedSetControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||||
|
{
|
||||||
|
var persistenceClientFactory = scope.ServiceProvider
|
||||||
|
.GetRequiredService<PersistenceClientFactory>();
|
||||||
|
|
||||||
|
client = persistenceClientFactory.GetClient<ITimestampedSetClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InsertRange()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(10, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal(testSets.Count(), response.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_without_filter()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
int count = 10;
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
var insertResponse = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.Get(idDiscriminator, null, null, 0, int.MaxValue);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
var items = response.Content!;
|
||||||
|
Assert.Equal(count, items.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_with_filter_props()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
int count = 10;
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
var insertResponse = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
string[] props = ["A"];
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.Get(idDiscriminator, null, props, 0, int.MaxValue);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
var items = response.Content!;
|
||||||
|
Assert.Equal(count, items.Count());
|
||||||
|
foreach ( var item in items )
|
||||||
|
{
|
||||||
|
Assert.Single(item.Set);
|
||||||
|
var kv = item.Set.First();
|
||||||
|
Assert.Equal("A", kv.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_geDate()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
int count = 10;
|
||||||
|
var dateMin = DateTimeOffset.Now;
|
||||||
|
var dateMax = DateTimeOffset.Now.AddSeconds(count);
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
var insertResponse = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
var tail = testSets.OrderBy(t => t.Timestamp).Skip(count / 2).Take(int.MaxValue);
|
||||||
|
var geDate = tail.First().Timestamp;
|
||||||
|
var tolerance = TimeSpan.FromSeconds(1);
|
||||||
|
var expectedCount = tail.Count();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.Get(idDiscriminator, geDate, null, 0, int.MaxValue);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
var items = response.Content!;
|
||||||
|
Assert.Equal(expectedCount, items.Count());
|
||||||
|
var minDate = items.Min(t => t.Timestamp);
|
||||||
|
Assert.Equal(geDate, geDate, tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_with_skip_take()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
int count = 10;
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
var insertResponse = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
var expectedCount = count / 2;
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.Get(idDiscriminator, null, null, 2, expectedCount);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
var items = response.Content!;
|
||||||
|
Assert.Equal(expectedCount, items.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_with_big_skip_take()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
var expectedCount = 1;
|
||||||
|
int count = 10 + expectedCount;
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
var insertResponse = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.Get(idDiscriminator, null, null, count - expectedCount, count);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
var items = response.Content!;
|
||||||
|
Assert.Equal(expectedCount, items.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetLast()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
int count = 10;
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
var insertResponse = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
var expectedCount = 8;
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.GetLast(idDiscriminator, null, expectedCount);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
var items = response.Content!;
|
||||||
|
Assert.Equal(expectedCount, items.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetDatesRange()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
int count = 10;
|
||||||
|
var dateMin = DateTimeOffset.Now;
|
||||||
|
var dateMax = DateTimeOffset.Now.AddSeconds(count-1);
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
var insertResponse = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
var tolerance = TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.GetDatesRange(idDiscriminator);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
var range = response.Content!;
|
||||||
|
Assert.Equal(dateMin, range.From, tolerance);
|
||||||
|
Assert.Equal(dateMax, range.To, tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Count()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
int count = 144;
|
||||||
|
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
|
||||||
|
var insertResponse = await client.InsertRange(idDiscriminator, testSets);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var response = await client.Count(idDiscriminator);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal(count, response.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<TimestampedSetDto> Generate(int n, DateTimeOffset from)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
yield return new TimestampedSetDto
|
||||||
|
(
|
||||||
|
from.AddSeconds(i),
|
||||||
|
new Dictionary<string, object>{
|
||||||
|
{"A", i },
|
||||||
|
{"B", i * 1.1 },
|
||||||
|
{"C", $"Any{i}" },
|
||||||
|
{"D", DateTimeOffset.Now},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
14
Persistence.IntegrationTests/EFCoreExtensions.cs
Normal file
14
Persistence.IntegrationTests/EFCoreExtensions.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
|
||||||
|
namespace Persistence.IntegrationTests;
|
||||||
|
public static class EFCoreExtensions
|
||||||
|
{
|
||||||
|
public static void CleanupDbSet<T>(this DbContext dbContext)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
var dbset = dbContext.Set<T>();
|
||||||
|
dbset.RemoveRange(dbset);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
<PackageReference Include="Refit" Version="8.0.0" />
|
<PackageReference Include="Refit" Version="8.0.0" />
|
||||||
|
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||||
<PackageReference Include="xunit" Version="2.9.2" />
|
<PackageReference Include="xunit" Version="2.9.2" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,6 +25,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Persistence.API\Persistence.API.csproj" />
|
<ProjectReference Include="..\Persistence.API\Persistence.API.csproj" />
|
||||||
|
<ProjectReference Include="..\Persistence.Client\Persistence.Client.csproj" />
|
||||||
|
<ProjectReference Include="..\Persistence.Database.Postgres\Persistence.Database.Postgres.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
19
Persistence.IntegrationTests/TestHttpClientFactory.cs
Normal file
19
Persistence.IntegrationTests/TestHttpClientFactory.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace Persistence.IntegrationTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Фабрика HTTP клиентов для интеграционных тестов
|
||||||
|
/// </summary>
|
||||||
|
public class TestHttpClientFactory : IHttpClientFactory
|
||||||
|
{
|
||||||
|
private readonly WebAppFactoryFixture factory;
|
||||||
|
|
||||||
|
public TestHttpClientFactory(WebAppFactoryFixture factory)
|
||||||
|
{
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
public HttpClient CreateClient(string name)
|
||||||
|
{
|
||||||
|
return factory.CreateClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,60 +1,56 @@
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Mvc.Testing;
|
using Microsoft.AspNetCore.Mvc.Testing;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Persistence.Database.Model;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Persistence.API;
|
using Persistence.API;
|
||||||
using Refit;
|
using Persistence.Database;
|
||||||
using System.Net.Http.Headers;
|
using Persistence.Client;
|
||||||
using System.Text.Json;
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Database.Postgres;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
namespace Persistence.IntegrationTests;
|
namespace Persistence.IntegrationTests;
|
||||||
public class WebAppFactoryFixture : WebApplicationFactory<Startup>
|
public class WebAppFactoryFixture : WebApplicationFactory<Startup>
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
|
private string connectionString = string.Empty;
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
PropertyNameCaseInsensitive = true,
|
|
||||||
//Converters = { new ValidationResultConverter() }
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions));
|
|
||||||
|
|
||||||
private readonly string connectionString;
|
|
||||||
|
|
||||||
public WebAppFactoryFixture()
|
|
||||||
{
|
|
||||||
var configuration = new ConfigurationBuilder()
|
|
||||||
.AddJsonFile("appsettings.Tests.json")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var dbConnection = configuration.GetSection("DbConnection").Get<DbConnection>()!;
|
|
||||||
connectionString = dbConnection.GetConnectionString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||||
{
|
{
|
||||||
builder.ConfigureServices(services =>
|
builder.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
|
{
|
||||||
|
config.AddJsonFile("appsettings.Tests.json");
|
||||||
|
|
||||||
|
var dbConnection = config.Build().GetSection("DbConnection").Get<DbConnection>()!;
|
||||||
|
connectionString = dbConnection.GetConnectionString();
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<PersistenceDbContext>));
|
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<PersistenceDbContext>));
|
||||||
|
|
||||||
if (descriptor != null)
|
if (descriptor != null)
|
||||||
services.Remove(descriptor);
|
services.Remove(descriptor);
|
||||||
|
services.AddDbContext<PersistenceDbContext>(options =>
|
||||||
services.AddDbContext<PersistenceDbContext>(options =>
|
|
||||||
options.UseNpgsql(connectionString));
|
options.UseNpgsql(connectionString));
|
||||||
|
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
services.RemoveAll<IHttpClientFactory>();
|
||||||
|
services.AddSingleton<IHttpClientFactory>(provider =>
|
||||||
|
{
|
||||||
|
return new TestHttpClientFactory(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddSingleton<PersistenceClientFactory>();
|
||||||
|
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
using var scope = serviceProvider.CreateScope();
|
using var scope = serviceProvider.CreateScope();
|
||||||
var scopedServices = scope.ServiceProvider;
|
var scopedServices = scope.ServiceProvider;
|
||||||
var dbContext = scopedServices.GetRequiredService<PersistenceDbContext>();
|
|
||||||
|
|
||||||
//dbContext.Database.EnsureCreatedAndMigrated();
|
var dbContext = scopedServices.GetRequiredService<PersistenceDbContext>();
|
||||||
//dbContext.Deposits.AddRange(Data.Defaults.Deposits);
|
dbContext.Database.EnsureCreatedAndMigrated();
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask DisposeAsync()
|
public override async ValueTask DisposeAsync()
|
||||||
@ -66,36 +62,4 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
|
|||||||
|
|
||||||
await dbContext.Database.EnsureDeletedAsync();
|
await dbContext.Database.EnsureDeletedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetHttpClient<T>(string uriSuffix)
|
|
||||||
{
|
|
||||||
var httpClient = CreateClient();
|
|
||||||
if (string.IsNullOrEmpty(uriSuffix))
|
|
||||||
return RestService.For<T>(httpClient, RefitSettings);
|
|
||||||
|
|
||||||
if (httpClient.BaseAddress is not null)
|
|
||||||
httpClient.BaseAddress = new Uri(httpClient.BaseAddress, uriSuffix);
|
|
||||||
|
|
||||||
return RestService.For<T>(httpClient, RefitSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
//public T GetAuthorizedHttpClient<T>(string uriSuffix)
|
|
||||||
//{
|
|
||||||
// var httpClient = GetAuthorizedHttpClient();
|
|
||||||
// if (string.IsNullOrEmpty(uriSuffix))
|
|
||||||
// return RestService.For<T>(httpClient, RefitSettings);
|
|
||||||
|
|
||||||
// if (httpClient.BaseAddress is not null)
|
|
||||||
// httpClient.BaseAddress = new Uri(httpClient.BaseAddress, uriSuffix);
|
|
||||||
|
|
||||||
// return RestService.For<T>(httpClient, RefitSettings);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private HttpClient GetAuthorizedHttpClient()
|
|
||||||
//{
|
|
||||||
// var httpClient = CreateClient();
|
|
||||||
// var jwtToken = ApiTokenHelper.GetAdminUserToken();
|
|
||||||
// httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
|
|
||||||
// return httpClient;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
199
Persistence.Repository/CyclicArray.cs
Normal file
199
Persistence.Repository/CyclicArray.cs
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Persistence.Repository;
|
||||||
|
/// <summary>
|
||||||
|
/// Цикличный массив
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public class CyclicArray<T> : IEnumerable<T>
|
||||||
|
{
|
||||||
|
readonly T[] array;
|
||||||
|
int used, current = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="capacity"></param>
|
||||||
|
public CyclicArray(int capacity)
|
||||||
|
{
|
||||||
|
array = new T[capacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Количество элементов в массиве
|
||||||
|
/// </summary>
|
||||||
|
public int Count => used;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить новый элемент<br/>
|
||||||
|
/// Если capacity достигнуто, то вытеснит самый первый элемент
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
public void Add(T item)
|
||||||
|
{
|
||||||
|
current = (++current) % array.Length;
|
||||||
|
array[current] = item;
|
||||||
|
if (used < array.Length)
|
||||||
|
used++;
|
||||||
|
UpdatedInvoke(current, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить новые элементы.<br/>
|
||||||
|
/// Если capacity достигнуто, то вытеснит самые первые элементы.<br/>
|
||||||
|
/// Не вызывает Updated!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="items"></param>
|
||||||
|
public void AddRange(IEnumerable<T> items)
|
||||||
|
{
|
||||||
|
var capacity = array.Length;
|
||||||
|
var newItems = items.TakeLast(capacity).ToArray();
|
||||||
|
if (newItems.Length == capacity)
|
||||||
|
{
|
||||||
|
Array.Copy(newItems, array, capacity);
|
||||||
|
current = capacity - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = (++current) % capacity;
|
||||||
|
var countToEndOfArray = capacity - current;
|
||||||
|
if (newItems.Length <= countToEndOfArray)
|
||||||
|
{
|
||||||
|
Array.Copy(newItems, 0, array, current, newItems.Length);
|
||||||
|
current += newItems.Length - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var firstStepLength = countToEndOfArray;
|
||||||
|
Array.Copy(newItems, 0, array, current, firstStepLength);
|
||||||
|
var secondStepCount = newItems.Length - firstStepLength;
|
||||||
|
Array.Copy(newItems, firstStepLength, array, 0, secondStepCount);
|
||||||
|
current = secondStepCount - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (used < capacity)
|
||||||
|
{
|
||||||
|
used += newItems.Length;
|
||||||
|
used = used > capacity ? capacity : used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Индекс
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public T this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (used == 0)
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
var i = (current + 1 + index) % used;
|
||||||
|
return array[i];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var devider = used > 0 ? used : array.Length;
|
||||||
|
var i = (current + 1 + index) % devider;
|
||||||
|
array[i] = value;
|
||||||
|
UpdatedInvoke(current, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// событие на изменение элемента в массиве
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<(int index, T value)>? Updated;
|
||||||
|
private void UpdatedInvoke(int index, T value)
|
||||||
|
{
|
||||||
|
Updated?.Invoke(this, (index, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Агрегирование значения по всему массиву
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="Tout"></typeparam>
|
||||||
|
/// <param name="func"></param>
|
||||||
|
/// <param name="startValue"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Tout Aggregate<Tout>(Func<T, Tout, Tout> func, Tout startValue)
|
||||||
|
{
|
||||||
|
Tout result = startValue;
|
||||||
|
for (int i = 0; i < used; i++)
|
||||||
|
result = func(this[i], result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
=> new CyclycListEnumerator<T>(array, current, used);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
=> GetEnumerator();
|
||||||
|
|
||||||
|
class CyclycListEnumerator<Te> : IEnumerator<Te>
|
||||||
|
{
|
||||||
|
private readonly Te[] array;
|
||||||
|
private readonly int used;
|
||||||
|
private readonly int first;
|
||||||
|
private int current = -1;
|
||||||
|
|
||||||
|
public CyclycListEnumerator(Te[] array, int first, int used)
|
||||||
|
{
|
||||||
|
this.array = new Te[array.Length];
|
||||||
|
array.CopyTo(this.array, 0);
|
||||||
|
this.used = used;
|
||||||
|
this.first = first;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Te Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsCurrentOk())
|
||||||
|
{
|
||||||
|
var i = (current + first + 1) % used;
|
||||||
|
return array[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object? IEnumerator.Current => Current;
|
||||||
|
|
||||||
|
public void Dispose() {; }
|
||||||
|
|
||||||
|
private bool IsCurrentOk() => current >= 0 && current < used;
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (current < used)
|
||||||
|
current++;
|
||||||
|
return IsCurrentOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
current = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Очистить весь массив
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
used = 0;
|
||||||
|
current = -1;
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,6 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||||||
namespace Persistence.Repository.Data;
|
namespace Persistence.Repository.Data;
|
||||||
public class DataSaubDto : ITimeSeriesAbstractDto
|
public class DataSaubDto : ITimeSeriesAbstractDto
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public DateTimeOffset Date { get; set; } = DateTimeOffset.UtcNow;
|
public DateTimeOffset Date { get; set; } = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
public int? Mode { get; set; }
|
public int? Mode { get; set; }
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
using Persistence.Models;
|
|
||||||
|
|
||||||
namespace Persistence.Repository.Data
|
|
||||||
{
|
|
||||||
internal class LogDto : IChangeLogAbstract
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public int IdAuthor { get; set; }
|
|
||||||
public int? IdNext { get; set; }
|
|
||||||
public int? IdPrevious { get; set; }
|
|
||||||
public int? IdEditor { get; set; }
|
|
||||||
public int IdState { get; set; }
|
|
||||||
public DateTimeOffset Creation { get; set; }
|
|
||||||
public DateTimeOffset? Obsolete { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
28
Persistence.Repository/Data/SetpointDto.cs
Normal file
28
Persistence.Repository/Data/SetpointDto.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
namespace Persistence.Repository.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Модель для работы с уставкой
|
||||||
|
/// </summary>
|
||||||
|
public class SetpointDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Идентификатор уставки
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Значение уставки
|
||||||
|
/// </summary>
|
||||||
|
public required object Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата сохранения уставки
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset Edit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ключ пользователя
|
||||||
|
/// </summary>
|
||||||
|
public int IdUser { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Persistence.Repositories;
|
|
||||||
using Persistence.Database.Model;
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Repositories;
|
||||||
using Persistence.Repository.Data;
|
using Persistence.Repository.Data;
|
||||||
using Persistence.Repository.Repositories;
|
using Persistence.Repository.Repositories;
|
||||||
using Persistence.Models;
|
|
||||||
|
|
||||||
namespace Persistence.Repository;
|
namespace Persistence.Repository;
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
@ -13,20 +10,16 @@ public static class DependencyInjection
|
|||||||
public static void MapsterSetup()
|
public static void MapsterSetup()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
MapsterSetup();
|
MapsterSetup();
|
||||||
|
|
||||||
string connectionStringName = "DefaultConnection";
|
|
||||||
|
|
||||||
services.AddDbContext<PersistenceDbContext>(options =>
|
|
||||||
options.UseNpgsql(configuration.GetConnectionString(connectionStringName)));
|
|
||||||
|
|
||||||
services.AddScoped<IPersistenceDbContext>(provider => provider.GetRequiredService<PersistenceDbContext>());
|
|
||||||
|
|
||||||
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataRepository<DataSaub, DataSaubDto>>();
|
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataRepository<DataSaub, DataSaubDto>>();
|
||||||
services.AddTransient<IChangeLogRepository<LogDto, ChangeLogDto<LogDto>>, ChangeLogRepository<ChangeLog, LogDto, ChangeLogDto<LogDto>>>();
|
services.AddTransient<ISetpointRepository, SetpointRepository>();
|
||||||
|
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataCachedRepository<DataSaub, DataSaubDto>>();
|
||||||
|
services.AddTransient<ITimestampedSetRepository, TimestampedSetRepository>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Persistence.Models;
|
|
||||||
using Persistence.Repositories;
|
|
||||||
|
|
||||||
namespace Persistence.Repository.Repositories
|
|
||||||
{
|
|
||||||
public abstract class ChangeLogRepository<TEntity, TDto, TChangeLogDto> : IChangeLogRepository<TDto, TChangeLogDto>
|
|
||||||
where TEntity : class
|
|
||||||
where TDto : class, IChangeLogAbstract, new()
|
|
||||||
where TChangeLogDto : ChangeLogDto<TDto>
|
|
||||||
{
|
|
||||||
private DbContext db;
|
|
||||||
|
|
||||||
public ChangeLogRepository(DbContext db)
|
|
||||||
{
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual IQueryable<TEntity> GetQueryReadOnly() => db.Set<TEntity>();
|
|
||||||
|
|
||||||
public Task<int> Clear(int idUser, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<int> ClearAndInsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IEnumerable<TChangeLogDto>> GetChangeLogForDate(DateTimeOffset? updateFrom, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IEnumerable<TDto>> GetCurrent(DateTimeOffset moment, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IEnumerable<DateOnly>> GetDatesChange(CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset dateBegin, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<int> InsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<int> MarkAsDeleted(int idUser, IEnumerable<int> ids, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<int> UpdateOrInsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<int> UpdateRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
73
Persistence.Repository/Repositories/SetpointRepository.cs
Normal file
73
Persistence.Repository/Repositories/SetpointRepository.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using Mapster;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Repositories;
|
||||||
|
|
||||||
|
namespace Persistence.Repository.Repositories
|
||||||
|
{
|
||||||
|
public class SetpointRepository : ISetpointRepository
|
||||||
|
{
|
||||||
|
private DbContext db;
|
||||||
|
public SetpointRepository(DbContext db)
|
||||||
|
{
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
|
||||||
|
|
||||||
|
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = GetQueryReadOnly();
|
||||||
|
var entities = await query
|
||||||
|
.Where(e => setpointKeys.Contains(e.Key))
|
||||||
|
.ToArrayAsync(token);
|
||||||
|
var dtos = entities.Select(e => e.Adapt<SetpointValueDto>());
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = GetQueryReadOnly();
|
||||||
|
var entities = await query
|
||||||
|
.Where(e => setpointKeys.Contains(e.Key))
|
||||||
|
.ToArrayAsync(token);
|
||||||
|
var filteredEntities = entities
|
||||||
|
.GroupBy(e => e.Key)
|
||||||
|
.Select(e => e.OrderBy(o => o.Created))
|
||||||
|
.Select(e => e.Where(e => e.Created <= historyMoment).Last());
|
||||||
|
var dtos = filteredEntities
|
||||||
|
.Select(e => e.Adapt<SetpointValueDto>());
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = GetQueryReadOnly();
|
||||||
|
var entities = await query
|
||||||
|
.Where(e => setpointKeys.Contains(e.Key))
|
||||||
|
.ToArrayAsync(token);
|
||||||
|
var dtos = entities
|
||||||
|
.GroupBy(e => e.Key)
|
||||||
|
.ToDictionary(e => e.Key, v => v.Select(z => z.Adapt<SetpointLogDto>()));
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Save(Guid setpointKey, object newValue, int idUser, CancellationToken token)
|
||||||
|
{
|
||||||
|
var entity = new Setpoint()
|
||||||
|
{
|
||||||
|
Key = setpointKey,
|
||||||
|
Value = newValue,
|
||||||
|
IdUser = idUser,
|
||||||
|
Created = DateTimeOffset.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
await db.Set<Setpoint>().AddAsync(entity, token);
|
||||||
|
await db.SaveChangesAsync(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
using Mapster;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Models;
|
||||||
|
|
||||||
|
namespace Persistence.Repository.Repositories;
|
||||||
|
|
||||||
|
public class TimeSeriesDataCachedRepository<TEntity, TDto> : TimeSeriesDataRepository<TEntity, TDto>
|
||||||
|
where TEntity : class, ITimestampedData, new()
|
||||||
|
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||||
|
{
|
||||||
|
public static TDto? FirstByDate { get; private set; }
|
||||||
|
public static CyclicArray<TDto> LastData { get; } = new CyclicArray<TDto>(CacheItemsCount);
|
||||||
|
|
||||||
|
private const int CacheItemsCount = 3600;
|
||||||
|
|
||||||
|
public TimeSeriesDataCachedRepository(DbContext db) : base(db)
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var firstDateItem = await base.GetFirstAsync(CancellationToken.None);
|
||||||
|
if (firstDateItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FirstByDate = firstDateItem;
|
||||||
|
|
||||||
|
var dtos = await base.GetLastAsync(CacheItemsCount, CancellationToken.None);
|
||||||
|
dtos = dtos.OrderBy(d => d.Date);
|
||||||
|
LastData.AddRange(dtos);
|
||||||
|
}).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset dateBegin, CancellationToken token)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (LastData.Count() == 0 || LastData[0].Date > dateBegin)
|
||||||
|
{
|
||||||
|
var dtos = await base.GetGtDate(dateBegin, token);
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = LastData
|
||||||
|
.Where(i => i.Date >= dateBegin);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<int> InsertRange(IEnumerable<TDto> dtos, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await base.InsertRange(dtos, token);
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
dtos = dtos.OrderBy(x => x.Date);
|
||||||
|
|
||||||
|
FirstByDate = dtos.First();
|
||||||
|
LastData.AddRange(dtos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<DatesRangeDto?> GetDatesRange(CancellationToken token)
|
||||||
|
{
|
||||||
|
if (FirstByDate == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return await Task.Run(() =>
|
||||||
|
{
|
||||||
|
return new DatesRangeDto
|
||||||
|
{
|
||||||
|
From = FirstByDate.Date,
|
||||||
|
To = LastData[^1].Date
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<IEnumerable<TDto>> GetResampledData(
|
||||||
|
DateTimeOffset dateBegin,
|
||||||
|
double intervalSec = 600d,
|
||||||
|
int approxPointsCount = 1024,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var dtos = LastData.Where(i => i.Date >= dateBegin);
|
||||||
|
if (LastData.Count == 0 || LastData[0].Date > dateBegin)
|
||||||
|
{
|
||||||
|
dtos = await base.GetGtDate(dateBegin, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dateEnd = dateBegin.AddSeconds(intervalSec);
|
||||||
|
dtos = dtos
|
||||||
|
.Where(i => i.Date <= dateEnd);
|
||||||
|
|
||||||
|
var ratio = dtos.Count() / approxPointsCount;
|
||||||
|
if (ratio > 1)
|
||||||
|
dtos = dtos
|
||||||
|
.Where((_, index) => index % ratio == 0);
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,12 @@
|
|||||||
using Mapster;
|
using Mapster;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Persistence.Database.Model;
|
||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
using Persistence.Repositories;
|
using Persistence.Repositories;
|
||||||
using Persistence.Database.Model;
|
|
||||||
using Persistence.Repository.Data;
|
|
||||||
|
|
||||||
namespace Persistence.Repository.Repositories;
|
namespace Persistence.Repository.Repositories;
|
||||||
public abstract class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataRepository<TDto>
|
public class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataRepository<TDto>
|
||||||
where TEntity : class
|
where TEntity : class, ITimestampedData, new()
|
||||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||||
{
|
{
|
||||||
private DbContext db;
|
private DbContext db;
|
||||||
@ -17,28 +16,32 @@ public abstract class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataR
|
|||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IQueryable<TEntity> GetQueryReadOnly() => db.Set<TEntity>();
|
protected virtual IQueryable<TEntity> GetQueryReadOnly() => this.db.Set<TEntity>();
|
||||||
|
|
||||||
public async Task<IEnumerable<TDto>> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
|
public virtual async Task<DatesRangeDto?> GetDatesRange(CancellationToken token)
|
||||||
{
|
{
|
||||||
var query = GetQueryReadOnly();
|
var query = GetQueryReadOnly();
|
||||||
|
var minDate = await query.MinAsync(o => o.Date, token);
|
||||||
|
var maxDate = await query.MaxAsync(o => o.Date, token);
|
||||||
|
|
||||||
|
return new DatesRangeDto
|
||||||
|
{
|
||||||
|
From = minDate,
|
||||||
|
To = maxDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset date, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = this.db.Set<TEntity>().Where(e => e.Date > date);
|
||||||
var entities = await query.ToArrayAsync(token);
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
var dtos = entities.Select(e => e.Adapt<TDto>());
|
var dtos = entities.Select(e => e.Adapt<TDto>());
|
||||||
|
|
||||||
return dtos;
|
return dtos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token)
|
public virtual async Task<int> InsertRange(IEnumerable<TDto> dtos, CancellationToken token)
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset date, CancellationToken token)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> InsertRange(IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
{
|
{
|
||||||
var entities = dtos.Select(d => d.Adapt<TEntity>());
|
var entities = dtos.Select(d => d.Adapt<TEntity>());
|
||||||
|
|
||||||
@ -47,4 +50,50 @@ public abstract class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataR
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task<IEnumerable<TDto>> GetLastAsync(int takeCount, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = GetQueryReadOnly()
|
||||||
|
.OrderByDescending(e => e.Date)
|
||||||
|
.Take(takeCount);
|
||||||
|
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
var dtos = entities.Select(e => e.Adapt<TDto>());
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<TDto?> GetFirstAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = GetQueryReadOnly()
|
||||||
|
.OrderBy(e => e.Date);
|
||||||
|
|
||||||
|
var entity = await query.FirstOrDefaultAsync(token);
|
||||||
|
|
||||||
|
if (entity == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var dto = entity.Adapt<TDto>();
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async virtual Task<IEnumerable<TDto>> GetResampledData(
|
||||||
|
DateTimeOffset dateBegin,
|
||||||
|
double intervalSec = 600d,
|
||||||
|
int approxPointsCount = 1024,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var dtos = await GetGtDate(dateBegin, token);
|
||||||
|
|
||||||
|
var dateEnd = dateBegin.AddSeconds(intervalSec);
|
||||||
|
dtos = dtos
|
||||||
|
.Where(i => i.Date <= dateEnd);
|
||||||
|
|
||||||
|
var ratio = dtos.Count() / approxPointsCount;
|
||||||
|
if (ratio > 1)
|
||||||
|
dtos = dtos
|
||||||
|
.Where((_, index) => index % ratio == 0);
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
121
Persistence.Repository/Repositories/TimestampedSetRepository.cs
Normal file
121
Persistence.Repository/Repositories/TimestampedSetRepository.cs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Persistence.Database.Entity;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Repositories;
|
||||||
|
|
||||||
|
namespace Persistence.Repository.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Репозиторий для хранения разных наборов данных временных рядов.
|
||||||
|
/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest.
|
||||||
|
/// idDiscriminator формируют клиенты и только им известно что они обозначают.
|
||||||
|
/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено.
|
||||||
|
/// </summary>
|
||||||
|
public class TimestampedSetRepository : ITimestampedSetRepository
|
||||||
|
{
|
||||||
|
private readonly DbContext db;
|
||||||
|
|
||||||
|
public TimestampedSetRepository(DbContext db)
|
||||||
|
{
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<int> InsertRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token)
|
||||||
|
{
|
||||||
|
var entities = sets.Select(set => new TimestampedSet(idDiscriminator, set.Timestamp.ToUniversalTime(), set.Set));
|
||||||
|
var dbSet = db.Set<TimestampedSet>();
|
||||||
|
dbSet.AddRange(entities);
|
||||||
|
return db.SaveChangesAsync(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<TimestampedSetDto>> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
||||||
|
{
|
||||||
|
var dbSet = db.Set<TimestampedSet>();
|
||||||
|
var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator);
|
||||||
|
|
||||||
|
if (geTimestamp.HasValue)
|
||||||
|
query = ApplyGeTimestamp(query, geTimestamp.Value);
|
||||||
|
|
||||||
|
query = query
|
||||||
|
.OrderBy(item => item.Timestamp)
|
||||||
|
.Skip(skip)
|
||||||
|
.Take(take);
|
||||||
|
|
||||||
|
var data = await Materialize(query, token);
|
||||||
|
|
||||||
|
if (columnNames is not null && columnNames.Any())
|
||||||
|
data = ReduceSetColumnsByNames(data, columnNames);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<TimestampedSetDto>> GetLast(Guid idDiscriminator, IEnumerable<string>? columnNames, int take, CancellationToken token)
|
||||||
|
{
|
||||||
|
var dbSet = db.Set<TimestampedSet>();
|
||||||
|
var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator);
|
||||||
|
|
||||||
|
query = query.OrderByDescending(entity => entity.Timestamp)
|
||||||
|
.Take(take)
|
||||||
|
.OrderBy(entity => entity.Timestamp);
|
||||||
|
|
||||||
|
var data = await Materialize(query, token);
|
||||||
|
|
||||||
|
if (columnNames is not null && columnNames.Any())
|
||||||
|
data = ReduceSetColumnsByNames(data, columnNames);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<int> Count(Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var dbSet = db.Set<TimestampedSet>();
|
||||||
|
var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator);
|
||||||
|
return query.CountAsync(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<TimestampedSet>()
|
||||||
|
.GroupBy(entity => entity.IdDiscriminator)
|
||||||
|
.Select(group => new
|
||||||
|
{
|
||||||
|
Min = group.Min(entity => entity.Timestamp),
|
||||||
|
Max = group.Max(entity => entity.Timestamp),
|
||||||
|
});
|
||||||
|
|
||||||
|
var item = await query.FirstOrDefaultAsync(token);
|
||||||
|
if (item is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new DatesRangeDto
|
||||||
|
{
|
||||||
|
From = item.Min,
|
||||||
|
To = item.Max,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IEnumerable<TimestampedSetDto>> Materialize(IQueryable<TimestampedSet> query, CancellationToken token)
|
||||||
|
{
|
||||||
|
var dtoQuery = query.Select(entity => new TimestampedSetDto(entity.Timestamp, entity.Set));
|
||||||
|
var dtos = await dtoQuery.ToArrayAsync(token);
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IQueryable<TimestampedSet> ApplyGeTimestamp(IQueryable<TimestampedSet> query, DateTimeOffset geTimestamp)
|
||||||
|
{
|
||||||
|
var geTimestampUtc = geTimestamp.ToUniversalTime();
|
||||||
|
return query.Where(entity => entity.Timestamp >= geTimestampUtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<TimestampedSetDto> ReduceSetColumnsByNames(IEnumerable<TimestampedSetDto> query, IEnumerable<string> columnNames)
|
||||||
|
{
|
||||||
|
var newQuery = query
|
||||||
|
.Select(entity => new TimestampedSetDto(
|
||||||
|
entity.Timestamp,
|
||||||
|
entity.Set
|
||||||
|
.Where(prop => columnNames.Contains(prop.Key))
|
||||||
|
.ToDictionary(prop => prop.Key, prop => prop.Value)
|
||||||
|
));
|
||||||
|
return newQuery;
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Persistence.Database;
|
|
||||||
using Persistence.Database.Model;
|
|
||||||
|
|
||||||
namespace Persistence.Repository;
|
|
||||||
public class Startup
|
|
||||||
{
|
|
||||||
public static void BeforeRunHandler(IHost host)
|
|
||||||
{
|
|
||||||
using var scope = host.Services.CreateScope();
|
|
||||||
var provider = scope.ServiceProvider;
|
|
||||||
|
|
||||||
var context = provider.GetRequiredService<DbContext>();
|
|
||||||
context.Database.EnsureCreatedAndMigrated();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Repository", "P
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Database", "Persistence.Database\Persistence.Database.csproj", "{F77475D1-D074-407A-9D69-2FADDDAE2056}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Database", "Persistence.Database\Persistence.Database.csproj", "{F77475D1-D074-407A-9D69-2FADDDAE2056}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Persistence.IntegrationTests", "Persistence.IntegrationTests\Persistence.IntegrationTests.csproj", "{10752C25-3773-4081-A1F2-215A1D950126}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.IntegrationTests", "Persistence.IntegrationTests\Persistence.IntegrationTests.csproj", "{10752C25-3773-4081-A1F2-215A1D950126}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Database.Postgres", "Persistence.Database.Postgres\Persistence.Database.Postgres.csproj", "{CC284D27-162D-490C-B6CF-74D666B7C5F3}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Persistence.Client", "Persistence.Client\Persistence.Client.csproj", "{84B68660-48E6-4974-A4E5-517552D9DE23}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -39,6 +43,14 @@ Global
|
|||||||
{10752C25-3773-4081-A1F2-215A1D950126}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{10752C25-3773-4081-A1F2-215A1D950126}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{10752C25-3773-4081-A1F2-215A1D950126}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{10752C25-3773-4081-A1F2-215A1D950126}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{10752C25-3773-4081-A1F2-215A1D950126}.Release|Any CPU.Build.0 = Release|Any CPU
|
{10752C25-3773-4081-A1F2-215A1D950126}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{84B68660-48E6-4974-A4E5-517552D9DE23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{84B68660-48E6-4974-A4E5-517552D9DE23}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{84B68660-48E6-4974-A4E5-517552D9DE23}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{84B68660-48E6-4974-A4E5-517552D9DE23}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -31,7 +31,7 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> AddAsync(TDto dto, CancellationToken token);
|
Task<ActionResult<int>> Add(TDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавить несколько записей
|
/// Добавить несколько записей
|
||||||
@ -39,7 +39,7 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> AddRangeAsync(IEnumerable<TDto> dtos, CancellationToken token);
|
Task<ActionResult<int>> AddRange(IEnumerable<TDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновить одну запись
|
/// Обновить одну запись
|
||||||
@ -47,7 +47,7 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> UpdateAsync(TDto dto, CancellationToken token);
|
Task<ActionResult<int>> Update(TDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновить несколько записей
|
/// Обновить несколько записей
|
||||||
@ -55,7 +55,7 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> UpdateRangeAsync(IEnumerable<TDto> dtos, CancellationToken token);
|
Task<ActionResult<int>> UpdateRange(IEnumerable<TDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Удалить одну запись
|
/// Удалить одну запись
|
||||||
@ -63,7 +63,7 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="id"></param>
|
/// <param name="id"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> DeleteAsync(int id, CancellationToken token);
|
Task<ActionResult<int>> Delete(int id, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Удалить несколько записей
|
/// Удалить несколько записей
|
||||||
@ -71,5 +71,5 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="ids"></param>
|
/// <param name="ids"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> DeleteRangeAsync(IEnumerable<int> ids, CancellationToken token);
|
Task<ActionResult<int>> DeleteRange(IEnumerable<int> ids, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ public interface IDictionaryElementApi<TDto> where TDto : class, new()
|
|||||||
/// <param name="dictionaryKey">ключ справочника</param>
|
/// <param name="dictionaryKey">ключ справочника</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<IEnumerable<TDto>>> GetAsync(Guid dictionaryKey, CancellationToken token);
|
Task<ActionResult<IEnumerable<TDto>>> Get(Guid dictionaryKey, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавить элемент в справочник
|
/// Добавить элемент в справочник
|
||||||
@ -22,7 +22,7 @@ public interface IDictionaryElementApi<TDto> where TDto : class, new()
|
|||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<Guid>> AddAsync(Guid dictionaryKey, TDto dto, CancellationToken token);
|
Task<ActionResult<Guid>> Add(Guid dictionaryKey, TDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Изменить одну запись
|
/// Изменить одну запись
|
||||||
@ -32,7 +32,7 @@ public interface IDictionaryElementApi<TDto> where TDto : class, new()
|
|||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<Guid>> UpdateAsync(Guid dictionaryKey, Guid dictionaryElementKey, TDto dto, CancellationToken token);
|
Task<ActionResult<Guid>> Update(Guid dictionaryKey, Guid dictionaryElementKey, TDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Удалить одну запись
|
/// Удалить одну запись
|
||||||
@ -41,5 +41,5 @@ public interface IDictionaryElementApi<TDto> where TDto : class, new()
|
|||||||
/// <param name="dictionaryElementKey">ключ элемента в справочнике</param>
|
/// <param name="dictionaryElementKey">ключ элемента в справочнике</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> DeleteAsync(Guid dictionaryKey, Guid dictionaryElementKey, CancellationToken token);
|
Task<ActionResult<int>> Delete(Guid dictionaryKey, Guid dictionaryElementKey, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ public interface ISetpointApi
|
|||||||
/// <param name="setpoitKeys">ключи уставок</param>
|
/// <param name="setpoitKeys">ключи уставок</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<IEnumerable<SetpointValueDto>>> GetCurrentAsync(IEnumerable<Guid> setpoitKeys, CancellationToken token);
|
Task<ActionResult<IEnumerable<SetpointValueDto>>> GetCurrent(IEnumerable<Guid> setpoitKeys, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить значения уставок за определенный момент времени
|
/// Получить значения уставок за определенный момент времени
|
||||||
@ -23,7 +23,7 @@ public interface ISetpointApi
|
|||||||
/// <param name="historyMoment">дата, на которую получаем данные</param>
|
/// <param name="historyMoment">дата, на которую получаем данные</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<IEnumerable<SetpointValueDto>>> GetHistoryAsync(IEnumerable<Guid> setpoitKeys, DateTimeOffset historyMoment, CancellationToken token);
|
Task<ActionResult<IEnumerable<SetpointValueDto>>> GetHistory(IEnumerable<Guid> setpoitKeys, DateTimeOffset historyMoment, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить историю изменений значений уставок
|
/// Получить историю изменений значений уставок
|
||||||
@ -31,7 +31,7 @@ public interface ISetpointApi
|
|||||||
/// <param name="setpoitKeys">ключи уставок</param>
|
/// <param name="setpoitKeys">ключи уставок</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLogAsync(IEnumerable<Guid> setpoitKeys, CancellationToken token);
|
Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog(IEnumerable<Guid> setpoitKeys, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Метод сохранения уставки
|
/// Метод сохранения уставки
|
||||||
@ -40,5 +40,5 @@ public interface ISetpointApi
|
|||||||
/// <param name="newValue">значение</param>
|
/// <param name="newValue">значение</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> SaveAsync(Guid setpointKey, object newValue, CancellationToken token);
|
Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ public interface ISyncApi<TDto> where TDto : class, new()
|
|||||||
/// <param name="take">количество записей</param>
|
/// <param name="take">количество записей</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<IEnumerable<TDto>>> GetPartAsync(DateTimeOffset dateBegin, int take = 24 * 60 * 60, CancellationToken token = default);
|
Task<ActionResult<IEnumerable<TDto>>> GetPart(DateTimeOffset dateBegin, int take = 24 * 60 * 60, CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||||
|
@ -19,5 +19,5 @@ public interface ITableDataApi<TDto, TRequest>
|
|||||||
/// <param name="request">параметры фильтрации</param>
|
/// <param name="request">параметры фильтрации</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<PaginationContainer<TDto>>> GetPageAsync(TRequest request, CancellationToken token);
|
Task<ActionResult<PaginationContainer<TDto>>> GetPage(TRequest request, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
@ -4,23 +4,27 @@ using Persistence.Models;
|
|||||||
namespace Persistence.API;
|
namespace Persistence.API;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Интерфейс для работы с API графиков
|
/// Базовый интерфейс для работы с временными рядами
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IGraphDataApi<TDto>
|
public interface ITimeSeriesBaseDataApi<TDto>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить список объектов с прореживанием, удовлетворящий диапазону дат
|
/// Получить список объектов с прореживанием, удовлетворяющий диапазону дат
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dateBegin">дата начала</param>
|
/// <param name="dateBegin">дата начала</param>
|
||||||
/// <param name="dateEnd">дата окончания</param>
|
/// <param name="dateEnd">дата окончания</param>
|
||||||
/// <param name="approxPointsCount"></param>
|
/// <param name="approxPointsCount"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<IEnumerable<TDto>>> GetThinnedDataAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, int approxPointsCount = 1024);
|
Task<IActionResult> GetResampledData(
|
||||||
|
DateTimeOffset dateBegin,
|
||||||
|
double intervalSec = 600d,
|
||||||
|
int approxPointsCount = 1024,
|
||||||
|
CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token);
|
Task<IActionResult> GetDatesRange(CancellationToken token);
|
||||||
}
|
}
|
@ -11,24 +11,16 @@ namespace Persistence.API;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Интерфейс для работы с API временных данных
|
/// Интерфейс для работы с API временных данных
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITimeSeriesDataApi<TDto>
|
public interface ITimeSeriesDataApi<TDto> : ITimeSeriesBaseDataApi<TDto>
|
||||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить список объектов, удовлетворяющий диапазон дат
|
/// Получить список объектов, удовлетворяющий диапазон дат
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dateBegin">дата начала</param>
|
/// <param name="dateBegin">дата начала</param>
|
||||||
/// <param name="dateEnd">дата окончания</param>
|
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IActionResult> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
|
Task<IActionResult> Get(DateTimeOffset dateBegin, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IActionResult> GetDatesRangeAsync(CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавление записей
|
/// Добавление записей
|
||||||
@ -36,7 +28,7 @@ public interface ITimeSeriesDataApi<TDto>
|
|||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IActionResult> InsertRangeAsync(IEnumerable<TDto> dtos, CancellationToken token);
|
Task<IActionResult> InsertRange(IEnumerable<TDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ public class ChangeLogDto<T> where T: class
|
|||||||
public DateTimeOffset Creation { get; set; }
|
public DateTimeOffset Creation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата устаревания (например при удалении)
|
/// Дата устаревания (например, при удалении)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTimeOffset? Obsolete { get; set; }
|
public DateTimeOffset? Obsolete { get; set; }
|
||||||
|
|
||||||
|
12
Persistence/Models/Configurations/AuthUser.cs
Normal file
12
Persistence/Models/Configurations/AuthUser.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Persistence.Models.Configurations;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Настройки credentials для авторизации
|
||||||
|
/// </summary>
|
||||||
|
public class AuthUser
|
||||||
|
{
|
||||||
|
public required string Username { get; set; }
|
||||||
|
public required string Password { get; set; }
|
||||||
|
public required string ClientId { get; set; }
|
||||||
|
public required string GrantType { get; set; }
|
||||||
|
}
|
18
Persistence/Models/Configurations/JwtParams.cs
Normal file
18
Persistence/Models/Configurations/JwtParams.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace Persistence.Models.Configurations
|
||||||
|
{
|
||||||
|
public static class JwtParams
|
||||||
|
{
|
||||||
|
private static readonly string KeyValue = "супер секретный ключ для шифрования";
|
||||||
|
public static SymmetricSecurityKey SecurityKey
|
||||||
|
{
|
||||||
|
get { return new SymmetricSecurityKey(Encoding.ASCII.GetBytes(KeyValue)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly string Issuer = "a";
|
||||||
|
|
||||||
|
public static readonly string Audience = "a";
|
||||||
|
}
|
||||||
|
}
|
10
Persistence/Models/Configurations/JwtToken.cs
Normal file
10
Persistence/Models/Configurations/JwtToken.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Persistence.Models.Configurations
|
||||||
|
{
|
||||||
|
public class JwtToken
|
||||||
|
{
|
||||||
|
[JsonPropertyName("access_token")]
|
||||||
|
public required string AccessToken { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -5,9 +5,19 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Persistence.Models;
|
namespace Persistence.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Диапазон дат
|
||||||
|
/// </summary>
|
||||||
public class DatesRangeDto
|
public class DatesRangeDto
|
||||||
{
|
{
|
||||||
public DateTimeOffset dateBegin { get; set; }
|
/// <summary>
|
||||||
|
/// Дата начала диапазона
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset From { get; set; }
|
||||||
|
|
||||||
public DateTimeOffset dateEnd { get; set; }
|
/// <summary>
|
||||||
|
/// Дата окончания диапазона
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset To { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -59,9 +59,4 @@ public interface IChangeLogAbstract
|
|||||||
/// Id заменяемой записи
|
/// Id заменяемой записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? IdPrevious { get; set; }
|
public int? IdPrevious { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Id последующей записи
|
|
||||||
/// </summary>
|
|
||||||
public int? IdNext { get; set; }
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
using System;
|
namespace Persistence.Models;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Интерфейс, описывающий временные данные
|
/// Интерфейс, описывающий временные данные
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
namespace Persistence.Models;
|
namespace Persistence.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Контейнер для поддержки постраничного просмотра таблиц
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
public class RequestDto
|
public class RequestDto
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Кол-во записей пропущенных с начала таблицы в запросе от api
|
||||||
|
/// </summary>
|
||||||
public int Skip { get; set; }
|
public int Skip { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Кол-во записей в запросе от api
|
||||||
|
/// </summary>
|
||||||
public int Take { get; set; }
|
public int Take { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Настройки сортировки
|
||||||
|
/// </summary>
|
||||||
public string SortSettings { get; set; } = string.Empty;
|
public string SortSettings { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.Models;
|
namespace Persistence.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Модель для описания лога уставки
|
||||||
|
/// </summary>
|
||||||
public class SetpointLogDto : SetpointValueDto
|
public class SetpointLogDto : SetpointValueDto
|
||||||
{
|
{
|
||||||
public DateTimeOffset Edit { get; set; }
|
/// <summary>
|
||||||
|
/// Дата сохранения уставки
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset Created { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ключ пользователя
|
||||||
|
/// </summary>
|
||||||
public int IdUser { get; set; }
|
public int IdUser { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.Models;
|
namespace Persistence.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Модель для хранения значения уставки
|
||||||
|
/// </summary>
|
||||||
public class SetpointValueDto
|
public class SetpointValueDto
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
/// Идентификатор уставки
|
||||||
public object Value { get; set; }
|
/// <summary>
|
||||||
|
/// </summary>
|
||||||
|
public Guid Key { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Значение уставки
|
||||||
|
/// </summary>
|
||||||
|
public required object Value { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
Persistence/Models/TimestampedSetDto.cs
Normal file
8
Persistence/Models/TimestampedSetDto.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Persistence.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// набор данных с отметкой времени
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Timestamp">отметка времени</param>
|
||||||
|
/// <param name="Set">набор данных</param>
|
||||||
|
public record TimestampedSetDto(DateTimeOffset Timestamp, IDictionary<string, object> Set);
|
@ -1,11 +1,8 @@
|
|||||||
using System;
|
namespace Persistence.Models;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.Models;
|
/// <summary>
|
||||||
|
/// Модель, описывающая пользователя
|
||||||
|
/// </summary>
|
||||||
public class UserDto
|
public class UserDto
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||||
|
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -7,7 +7,7 @@ namespace Persistence.Repositories;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDto"></typeparam>
|
/// <typeparam name="TDto"></typeparam>
|
||||||
public interface IChangeLogRepository<TDto, TChangeLogDto> : ISyncRepository<TDto>
|
public interface IChangeLogRepository<TDto, TChangeLogDto> : ISyncRepository<TDto>
|
||||||
where TDto : class, IChangeLogAbstract, new()
|
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||||
where TChangeLogDto : ChangeLogDto<TDto>
|
where TChangeLogDto : ChangeLogDto<TDto>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.Repositories;
|
namespace Persistence.Repositories;
|
||||||
|
|
||||||
@ -13,23 +7,30 @@ namespace Persistence.Repositories;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISetpointRepository
|
public interface ISetpointRepository
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Получить значения уставок по набору ключей
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setpointKeys"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить значения уставок за определенный момент времени
|
/// Получить значения уставок за определенный момент времени
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="setpoitKeys"></param>
|
/// <param name="setpointKeys"></param>
|
||||||
/// <param name="historyMoment">дата, на которую получаем данные</param>
|
/// <param name="historyMoment">дата, на которую получаем данные</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<SetpointValueDto>> GetHistoryAsync(IEnumerable<Guid> setpoitKeys, DateTimeOffset historyMoment, CancellationToken token);
|
Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить историю изменений значений уставок
|
/// Получить историю изменений значений уставок
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="setpoitKeys"></param>
|
/// <param name="setpointKeys"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLogAsync(IEnumerable<Guid> setpoitKeys, CancellationToken token);
|
Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Метод сохранения уставки
|
/// Метод сохранения уставки
|
||||||
@ -41,5 +42,5 @@ public interface ISetpointRepository
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// to do
|
/// to do
|
||||||
/// id User учесть в соответствующем методе репозитория
|
/// id User учесть в соответствующем методе репозитория
|
||||||
Task<int> SaveAsync(Guid setpointKey, int idUser, object newValue, CancellationToken token);
|
Task Save(Guid setpointKey, object newValue, int idUser, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
@ -15,5 +15,5 @@ public interface ITableDataRepository<TDto, TRequest>
|
|||||||
/// <param name="request">параметры фильтрации</param>
|
/// <param name="request">параметры фильтрации</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<TDto>> GetAsync(TRequest request, CancellationToken token);
|
Task<IEnumerable<TDto>> Get(TRequest request, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
@ -5,22 +5,25 @@ namespace Persistence.Repositories;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Интерфейс по работе с прореженными данными
|
/// Интерфейс по работе с прореженными данными
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IGraphDataRepository<TDto>
|
public interface ITimeSeriesBaseRepository<TDto>
|
||||||
where TDto : class, new()
|
where TDto : class, new()
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить список объектов с прореживанием
|
/// Получить список объектов с прореживанием
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dateBegin">дата начала</param>
|
/// <param name="dateBegin">дата начала</param>
|
||||||
/// <param name="dateEnd">дата окончания</param>
|
|
||||||
/// <param name="approxPointsCount"></param>
|
/// <param name="approxPointsCount"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<TDto>> GetThinnedDataAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, int approxPointsCount = 1024);
|
Task<IEnumerable<TDto>> GetResampledData(
|
||||||
|
DateTimeOffset dateBegin,
|
||||||
|
double intervalSec = 600d,
|
||||||
|
int approxPointsCount = 1024,
|
||||||
|
CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token);
|
Task<DatesRangeDto?> GetDatesRange(CancellationToken token);
|
||||||
}
|
}
|
@ -6,24 +6,9 @@ namespace Persistence.Repositories;
|
|||||||
/// Интерфейс по работе с временными данными
|
/// Интерфейс по работе с временными данными
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDto"></typeparam>
|
/// <typeparam name="TDto"></typeparam>
|
||||||
public interface ITimeSeriesDataRepository<TDto> : ISyncRepository<TDto>
|
public interface ITimeSeriesDataRepository<TDto> : ISyncRepository<TDto>, ITimeSeriesBaseRepository<TDto>
|
||||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
where TDto : class, ITimeSeriesAbstractDto, new()
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Получить страницу списка объектов
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dateBegin">дата начала</param>
|
|
||||||
/// <param name="dateEnd">дата окончания</param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<TDto>> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token);
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавление записей
|
/// Добавление записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
59
Persistence/Repositories/ITimestampedSetRepository.cs
Normal file
59
Persistence/Repositories/ITimestampedSetRepository.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using Persistence.Models;
|
||||||
|
|
||||||
|
namespace Persistence.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Репозиторий для хранения разных наборов данных рядов.
|
||||||
|
/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest.
|
||||||
|
/// idDiscriminator формируют клиенты и только им известно что они обозначают.
|
||||||
|
/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITimestampedSetRepository
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Количество записей по указанному набору в БД. Для пагинации.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> Count(Guid idDiscriminator, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных с фильтрацией. Значение фильтра null - отключен
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="geTimestamp">Фильтр позднее даты</param>
|
||||||
|
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||||
|
/// <param name="skip"></param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IEnumerable<TimestampedSetDto>> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Диапазон дат за которые есть данные
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить последние данные
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IEnumerable<TimestampedSetDto>> GetLast(Guid idDiscriminator, IEnumerable<string>? columnNames, int take, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавление новых данных
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">Дискриминатор (идентификатор) набора</param>
|
||||||
|
/// <param name="sets"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> InsertRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token);
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
namespace Persistence.Services;
|
namespace Persistence.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Сервис по работе с БД
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal interface IArchiveService
|
internal interface IArchiveService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Переименование БД
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="connectionString"></param>
|
/// <param name="connectionString"></param>
|
||||||
/// <param name="databaseName"></param>
|
/// <param name="databaseName"></param>
|
||||||
@ -15,7 +15,7 @@ internal interface IArchiveService
|
|||||||
Task RenameDatabase(string connectionString, string databaseName, CancellationToken token);
|
Task RenameDatabase(string connectionString, string databaseName, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Создание БД
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="connectionString"></param>
|
/// <param name="connectionString"></param>
|
||||||
/// <param name="databaseName"></param>
|
/// <param name="databaseName"></param>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace Persistence.Services;
|
namespace Persistence.Services;
|
||||||
|
|
||||||
public interface ITimeSeriesDataObserverService
|
public interface ITimeSeriesDataObserverService
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user