Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
66576e94f7 | |||
|
b894f61b66 | ||
|
e15692c2ad |
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"DefaultConnection": "Host=db:5432;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*",
|
|
||||||
"NeedUseKeyCloak": false,
|
|
||||||
"KeyCloakAuthentication": {
|
|
||||||
"Audience": "account",
|
|
||||||
"Host": "http://192.168.0.10:8321/realms/Persistence"
|
|
||||||
},
|
|
||||||
"AuthUser": {
|
|
||||||
"username": "myuser",
|
|
||||||
"password": 12345,
|
|
||||||
"clientId": "webapi",
|
|
||||||
"grantType": "password",
|
|
||||||
"http://schemas.xmlsoap.org/ws/2005/05/identity /claims/nameidentifier": "7d9f3574-6574-4ca3-845a-0276eb4aa8f6"
|
|
||||||
},
|
|
||||||
"ClientUrl": "http://localhost:5000/"
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
networks:
|
|
||||||
persistence:
|
|
||||||
external: false
|
|
||||||
|
|
||||||
services:
|
|
||||||
db:
|
|
||||||
image: timescale/timescaledb:latest-pg16
|
|
||||||
container_name: some-timescaledb-16
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
- POSTGRES_PASSWORD=postgres
|
|
||||||
networks:
|
|
||||||
- persistence
|
|
||||||
ports:
|
|
||||||
- "5462:5432"
|
|
||||||
volumes:
|
|
||||||
- ./db:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
image: git.ddrilling.ru/ddrilling/persistence:latest
|
|
||||||
container_name: persistence
|
|
||||||
restart: always
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
networks:
|
|
||||||
- persistence
|
|
||||||
ports:
|
|
||||||
- "1111:8080"
|
|
||||||
volumes:
|
|
||||||
- ./appsettings.json:/app/appsettings.json
|
|
||||||
|
|
@ -1,174 +1,133 @@
|
|||||||
using DD.Persistence.API;
|
|
||||||
using DD.Persistence.Models.ChangeLog;
|
|
||||||
using DD.Persistence.Models.Common;
|
|
||||||
using DD.Persistence.Models.Requests;
|
|
||||||
using DD.Persistence.Services;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using DD.Persistence.Models;
|
||||||
|
using DD.Persistence.Models.Requests;
|
||||||
|
using DD.Persistence.Repositories;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using DD.Persistence.Models.Common;
|
||||||
|
|
||||||
|
namespace DD.Persistence.API.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Контроллер по работе с журналом изменений
|
|
||||||
/// </summary>
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class ChangeLogController : ControllerBase, IChangeLogApi
|
public class ChangeLogController : ControllerBase, IChangeLogApi
|
||||||
{
|
{
|
||||||
private readonly ChangeLogService service;
|
private readonly IChangeLogRepository repository;
|
||||||
|
|
||||||
/// <summary>
|
public ChangeLogController(IChangeLogRepository repository)
|
||||||
/// ctor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="service"></param>
|
|
||||||
public ChangeLogController(ChangeLogService service)
|
|
||||||
{
|
{
|
||||||
this.service = service;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавить записи в журнал изменений по дискриминатору
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="dtos"></param>
|
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost("{idDiscriminator}")]
|
[HttpPost("{idDiscriminator}")]
|
||||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
||||||
public async Task<IActionResult> AddRange(
|
public async Task<IActionResult> Add(
|
||||||
[FromRoute] Guid idDiscriminator,
|
[FromRoute] Guid idDiscriminator,
|
||||||
[FromBody] IEnumerable<IDictionary<string, object>> dtos,
|
[FromBody] ChangeLogValuesDto dto,
|
||||||
string? comment,
|
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
var userId = User.GetUserId<Guid>();
|
||||||
var changeLogCommitRequest = new ChangeLogCommitCreateRequest
|
var result = await repository.AddRange(userId, idDiscriminator, [dto], token);
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(Add), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("range/{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
||||||
|
public async Task<IActionResult> AddRange(
|
||||||
|
[FromRoute] Guid idDiscriminator,
|
||||||
|
[FromBody] IEnumerable<ChangeLogValuesDto> dtos,
|
||||||
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
IdAuthor = userId,
|
var userId = User.GetUserId<Guid>();
|
||||||
Comment = comment,
|
var result = await repository.AddRange(userId, idDiscriminator, dtos, token);
|
||||||
DiscriminatorId = idDiscriminator,
|
|
||||||
};
|
|
||||||
var result = await service.AddRange(changeLogCommitRequest, dtos, token);
|
|
||||||
|
|
||||||
return CreatedAtAction(nameof(AddRange), result);
|
return CreatedAtAction(nameof(AddRange), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Удалить записи в журнале изменений
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="ids"></param>
|
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpDelete]
|
[HttpDelete]
|
||||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
public async Task<IActionResult> DeleteRange(Guid idDiscriminator, IEnumerable<Guid> ids, string comment, CancellationToken token)
|
public async Task<IActionResult> Delete(Guid id, CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
var userId = User.GetUserId<Guid>();
|
||||||
var changeLogCommitRequest = new ChangeLogCommitCreateRequest(idDiscriminator, userId, comment);
|
var result = await repository.MarkAsDeleted(userId, [id], token);
|
||||||
var result = await service.MarkAsDeleted(ids, changeLogCommitRequest, token);
|
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpDelete("range")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> DeleteRange(IEnumerable<Guid> ids, CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.MarkAsDeleted(userId, ids, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Очистить все записи в журнале изменений (по дискриминатору) и добавить новые
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="dtos"></param>
|
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost("replace/{idDiscriminator}")]
|
[HttpPost("replace/{idDiscriminator}")]
|
||||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
public async Task<IActionResult> ClearAndAddRange(
|
public async Task<IActionResult> ClearAndAddRange(
|
||||||
[FromRoute] Guid idDiscriminator,
|
[FromRoute] Guid idDiscriminator,
|
||||||
[FromBody] IEnumerable<IDictionary<string, object>> dtos,
|
[FromBody] IEnumerable<ChangeLogValuesDto> dtos,
|
||||||
string comment,
|
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
var userId = User.GetUserId<Guid>();
|
||||||
var changeLogCommitRequest = new ChangeLogCommitCreateRequest(idDiscriminator, userId, comment);
|
var result = await repository.ClearAndAddRange(userId, idDiscriminator, dtos, token);
|
||||||
var result = await service.ClearAndAddRange(changeLogCommitRequest, dtos, token);
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// сохранить изменения в записях журнала изменений
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="dtos"></param>
|
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
public async Task<IActionResult> Update(
|
||||||
public async Task<IActionResult> UpdateRange(
|
ChangeLogValuesDto dto,
|
||||||
Guid idDiscriminator,
|
|
||||||
IEnumerable<ChangeLogBaseDto> dtos,
|
|
||||||
string comment,
|
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
var userId = User.GetUserId<Guid>();
|
||||||
var changeLogCommitRequest = new ChangeLogCommitCreateRequest(idDiscriminator, userId, comment);
|
var result = await repository.UpdateRange(userId, [dto], token);
|
||||||
var result = await service.UpdateRange(changeLogCommitRequest, dtos, token);
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("range")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> UpdateRange(
|
||||||
|
IEnumerable<ChangeLogValuesDto> dtos,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.UpdateRange(userId, dtos, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение актуальных записей (с пагинацией)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="paginationRequest"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("{idDiscriminator}")]
|
[HttpGet("{idDiscriminator}")]
|
||||||
[ProducesResponseType(typeof(PaginationContainer<ChangeLogBaseDto>), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(PaginationContainer<ChangeLogValuesDto>), (int)HttpStatusCode.OK)]
|
||||||
public async Task<IActionResult> GetCurrent(
|
public async Task<IActionResult> GetCurrent(
|
||||||
[FromRoute] Guid idDiscriminator,
|
[FromRoute] Guid idDiscriminator,
|
||||||
[FromQuery] PaginationRequest paginationRequest,
|
[FromQuery] PaginationRequest paginationRequest,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var moment = new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
var moment = new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||||
var result = await service.GetByDate(idDiscriminator, moment, paginationRequest, token);
|
var result = await repository.GetByDate(idDiscriminator, moment, paginationRequest, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение записей на определенный момент времени (с пагинацией)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="moment"></param>
|
|
||||||
/// <param name="paginationRequest"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("moment/{idDiscriminator}")]
|
[HttpGet("moment/{idDiscriminator}")]
|
||||||
[ProducesResponseType(typeof(PaginationContainer<ChangeLogBaseDto>), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(PaginationContainer<ChangeLogValuesDto>), (int)HttpStatusCode.OK)]
|
||||||
public async Task<IActionResult> GetByDate(
|
public async Task<IActionResult> GetByDate(
|
||||||
[FromRoute] Guid idDiscriminator,
|
[FromRoute] Guid idDiscriminator,
|
||||||
DateTimeOffset moment,
|
DateTimeOffset moment,
|
||||||
[FromQuery] PaginationRequest paginationRequest,
|
[FromQuery] PaginationRequest paginationRequest,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await service.GetByDate(idDiscriminator, moment, paginationRequest, token);
|
var result = await repository.GetByDate(idDiscriminator, moment, paginationRequest, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение измененных записей за период времени
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="dateBegin"></param>
|
|
||||||
/// <param name="dateEnd"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("history/{idDiscriminator}")]
|
[HttpGet("history/{idDiscriminator}")]
|
||||||
[ProducesResponseType(typeof(IEnumerable<ChangeLogDto>), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(IEnumerable<ChangeLogDto>), (int)HttpStatusCode.OK)]
|
||||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
@ -178,91 +137,41 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
|
|||||||
DateTimeOffset dateEnd,
|
DateTimeOffset dateEnd,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await service.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, token);
|
var result = await repository.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("datesChange/{idDiscriminator}")]
|
[HttpGet("datesChange/{idDiscriminator}")]
|
||||||
[ProducesResponseType(typeof(IEnumerable<DateOnly>), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(IEnumerable<DateOnly>), (int)HttpStatusCode.OK)]
|
||||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
public async Task<IActionResult> GetDatesChange([FromRoute] Guid idDiscriminator, CancellationToken token)
|
public async Task<IActionResult> GetDatesChange([FromRoute] Guid idDiscriminator, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await service.GetDatesChange(idDiscriminator, token);
|
var result = await repository.GetDatesChange(idDiscriminator, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение данных, начиная с определенной даты
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="dateBegin"></param>
|
|
||||||
/// <param name="take"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("part/{idDiscriminator}")]
|
[HttpGet("part/{idDiscriminator}")]
|
||||||
[ProducesResponseType(typeof(IEnumerable<ChangeLogBaseDto>), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(IEnumerable<ChangeLogValuesDto>), (int)HttpStatusCode.OK)]
|
||||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
public async Task<ActionResult<IEnumerable<ChangeLogBaseDto>>> GetPart([FromRoute] Guid idDiscriminator, DateTimeOffset dateBegin, int take = 86400, CancellationToken token = default)
|
public async Task<ActionResult<IEnumerable<ChangeLogValuesDto>>> GetPart([FromRoute] Guid idDiscriminator, DateTimeOffset dateBegin, int take = 86400, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var result = await service.GetGtDate(idDiscriminator, dateBegin, token);
|
var result = await repository.GetGtDate(idDiscriminator, dateBegin, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("datesRange/{idDiscriminator}")]
|
[HttpGet("datesRange/{idDiscriminator}")]
|
||||||
[ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
|
||||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync([FromRoute] Guid idDiscriminator, CancellationToken token)
|
public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync([FromRoute] Guid idDiscriminator, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await service.GetDatesRange(idDiscriminator, token);
|
var result = await repository.GetDatesRange(idDiscriminator, token);
|
||||||
|
|
||||||
if (result is null)
|
if (result is null)
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Метод, который возвращает статистику пользователя по количеству изменений в разрезе дней
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("statistics")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<ChangeLogStatisticsDto>), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<IActionResult> GetStatistics([FromQuery] ChangeLogQuery request, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = await service.GetStatistics(request, token);
|
|
||||||
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Метод, который возвращает историю изменений в разрезе дней
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("history")]
|
|
||||||
[ProducesResponseType(typeof(IEnumerable<ChangeLogCommitDto>), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<IActionResult> GetHistory([FromQuery] ChangeLogQuery request, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = await service.GetHistory(request, token);
|
|
||||||
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using DD.Persistence.Filter.Models.Abstractions;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models;
|
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
using DD.Persistence.Repositories;
|
using DD.Persistence.Repositories;
|
||||||
using DD.Persistence.Services.Interfaces;
|
using DD.Persistence.Services.Interfaces;
|
||||||
@ -46,7 +45,6 @@ public class TimestampedValuesController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="discriminatorIds">Набор дискриминаторов</param>
|
/// <param name="discriminatorIds">Набор дискриминаторов</param>
|
||||||
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
||||||
/// <param name="filterTree">Кастомный фильтр по набору значений</param>
|
|
||||||
/// <param name="columnNames">Фильтр свойств набора</param>
|
/// <param name="columnNames">Фильтр свойств набора</param>
|
||||||
/// <param name="skip"></param>
|
/// <param name="skip"></param>
|
||||||
/// <param name="take"></param>
|
/// <param name="take"></param>
|
||||||
@ -54,14 +52,9 @@ public class TimestampedValuesController : ControllerBase
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
|
||||||
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> Get([FromQuery] IEnumerable<Guid> discriminatorIds,
|
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> Get([FromQuery] IEnumerable<Guid> discriminatorIds, DateTimeOffset? timestampBegin, [FromQuery] string[]? columnNames, int skip, int take, CancellationToken token)
|
||||||
DateTimeOffset? timestampBegin,
|
|
||||||
[FromQuery] TNode? filterTree,
|
|
||||||
[FromQuery] string[]? columnNames,
|
|
||||||
int skip, int take,
|
|
||||||
CancellationToken token)
|
|
||||||
{
|
{
|
||||||
var result = await timestampedValuesService.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token);
|
var result = await timestampedValuesService.Get(discriminatorIds, timestampBegin, columnNames, skip, take, token);
|
||||||
|
|
||||||
return result.Any() ? Ok(result) : NoContent();
|
return result.Any() ? Ok(result) : NoContent();
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\DD.Persistence.Database.Postgres\DD.Persistence.Database.Postgres.csproj" />
|
<ProjectReference Include="..\DD.Persistence.Database.Postgres\DD.Persistence.Database.Postgres.csproj" />
|
||||||
<ProjectReference Include="..\DD.Persistence.Database\DD.Persistence.Database.csproj" />
|
<ProjectReference Include="..\DD.Persistence.Database\DD.Persistence.Database.csproj" />
|
||||||
|
<ProjectReference Include="..\DD.Persistence.Repository\DD.Persistence.Repository.csproj" />
|
||||||
<ProjectReference Include="..\DD.Persistence\DD.Persistence.csproj" />
|
<ProjectReference Include="..\DD.Persistence\DD.Persistence.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Docs\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
using DD.Persistence.Filter.Models.Abstractions;
|
using Mapster;
|
||||||
using DD.Persistence.Models.Configurations;
|
|
||||||
using DD.Persistence.Services;
|
|
||||||
using DD.Persistence.Services.Interfaces;
|
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Microsoft.OpenApi.Any;
|
using Microsoft.OpenApi.Any;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
using DD.Persistence.Models;
|
||||||
|
using DD.Persistence.Models.Configurations;
|
||||||
|
using DD.Persistence.Services;
|
||||||
|
using DD.Persistence.Services.Interfaces;
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
|
using DD.Persistence.Database.Entity;
|
||||||
|
|
||||||
namespace DD.Persistence.API;
|
namespace DD.Persistence.API;
|
||||||
|
|
||||||
@ -28,7 +30,6 @@ public static class DependencyInjection
|
|||||||
new OpenApiSchema {Type = "number", Format = "float" }
|
new OpenApiSchema {Type = "number", Format = "float" }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
c.MapType<TNode>(() => new OpenApiSchema { Type = "string" });
|
|
||||||
|
|
||||||
c.CustomOperationIds(e =>
|
c.CustomOperationIds(e =>
|
||||||
{
|
{
|
||||||
@ -53,7 +54,6 @@ public static class DependencyInjection
|
|||||||
{
|
{
|
||||||
services.AddTransient<IWitsDataService, WitsDataService>();
|
services.AddTransient<IWitsDataService, WitsDataService>();
|
||||||
services.AddTransient<ITimestampedValuesService, TimestampedValuesService>();
|
services.AddTransient<ITimestampedValuesService, TimestampedValuesService>();
|
||||||
services.AddTransient<ChangeLogService>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Authentication
|
#region Authentication
|
||||||
|
@ -1,359 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36" version="24.8.3">
|
|
||||||
<diagram name="Страница — 1" id="7k5Wemfp-yc9piGHxsiE">
|
|
||||||
<mxGraphModel dx="2049" dy="1054" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1300" pageHeight="1050" math="0" shadow="0">
|
|
||||||
<root>
|
|
||||||
<mxCell id="0" />
|
|
||||||
<mxCell id="1" parent="0" />
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-1" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="355" y="930" as="sourcePoint" />
|
|
||||||
<mxPoint x="355" y="210" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-2" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="930" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="210" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-4" value="<b>FRONT</b>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;strokeColor=#432D57;fontColor=#ffffff;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="325" y="180" width="60" height="30" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-5" value="<b>BACK</b>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="525" y="180" width="60" height="30" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-7" value="<b>CACHE</b>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="725" y="180" width="60" height="30" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-8" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="955" y="930" as="sourcePoint" />
|
|
||||||
<mxPoint x="955" y="210" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-9" value="<b>COMMIT REPOSITORY</b>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="880" y="180" width="155" height="30" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-32" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;align=center;" edge="1" parent="1" target="_dnhcZFeje3u91oS2JwL-7">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="755" y="930" as="sourcePoint" />
|
|
||||||
<mxPoint x="756.5000000000002" y="300" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-52" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="300" as="sourcePoint" />
|
|
||||||
<mxPoint x="755" y="300" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-53" value="<span style="font-size: 12px; text-wrap-mode: wrap;">GetOrCreate(userId, message)</span>" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-52">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-67" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="755" y="310" as="sourcePoint" />
|
|
||||||
<mxPoint x="955" y="310.83" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-68" value="CreateCommit(userId, message)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-67">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-69" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="955" y="330" as="sourcePoint" />
|
|
||||||
<mxPoint x="755" y="330" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-70" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-69">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-88" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="1155" y="930" as="sourcePoint" />
|
|
||||||
<mxPoint x="1155" y="210" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-89" value="<b>CHANGE LOG REPOSITORY</b>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="1060" y="180" width="185" height="30" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-90" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="755" y="340" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="340" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-91" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-90">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-93" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="380" as="sourcePoint" />
|
|
||||||
<mxPoint x="1155" y="380" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-94" value="AddRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-93">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-96" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="1155" y="410" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="410" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-97" value="CREATED" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-96">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-100" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="420" as="sourcePoint" />
|
|
||||||
<mxPoint x="355" y="420" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-101" value="CREATED" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-100">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-102" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="420" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="290" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-104" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="355" y="290" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="290" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-105" value="Insert(items, message) [POST]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-104">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-107" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="354.13" y="420" as="sourcePoint" />
|
|
||||||
<mxPoint x="354.13" y="290" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-108" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="355" y="520" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="520" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-109" value="Update(items, message) [PUT]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-108">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-110" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="530" as="sourcePoint" />
|
|
||||||
<mxPoint x="755" y="530" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-111" value="<span style="font-size: 12px; text-wrap-mode: wrap;">GetOrCreate(userId, message)</span>" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-110">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-112" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="755" y="560" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="560" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-113" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-112">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-114" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="554.5699999999999" y="650" as="sourcePoint" />
|
|
||||||
<mxPoint x="554.5699999999999" y="520" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-115" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="610" as="sourcePoint" />
|
|
||||||
<mxPoint x="1155" y="610" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-116" value="UpdateRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-115">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-117" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="1155" y="640" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="640" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-118" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-117">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-119" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="650" as="sourcePoint" />
|
|
||||||
<mxPoint x="355" y="650" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-120" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-119">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-121" value="" style="edgeStyle=elbowEdgeStyle;elbow=horizontal;endArrow=classic;html=1;curved=0;rounded=0;endSize=8;startSize=8;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="755" y="530" as="sourcePoint" />
|
|
||||||
<mxPoint x="755" y="560" as="targetPoint" />
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="775" y="550" />
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-122" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="355" y="760" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="760" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-123" value="Delete(items, message) [DELETE]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-122">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-124" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="770" as="sourcePoint" />
|
|
||||||
<mxPoint x="755" y="770" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-125" value="<span style="font-size: 12px; text-wrap-mode: wrap;">GetOrCreate(userId, message)</span>" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-124">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-126" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="755" y="800" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="800" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-127" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-126">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-128" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="554.5699999999999" y="890" as="sourcePoint" />
|
|
||||||
<mxPoint x="554.5699999999999" y="760" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-129" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="850" as="sourcePoint" />
|
|
||||||
<mxPoint x="1155" y="850" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-130" value="UpdateRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-129">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-131" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="1155" y="880" as="sourcePoint" />
|
|
||||||
<mxPoint x="555" y="880" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-132" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-131">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-133" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="555" y="890" as="sourcePoint" />
|
|
||||||
<mxPoint x="355" y="890" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-134" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-133">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-135" value="" style="edgeStyle=elbowEdgeStyle;elbow=horizontal;endArrow=classic;html=1;curved=0;rounded=0;endSize=8;startSize=8;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="755" y="770" as="sourcePoint" />
|
|
||||||
<mxPoint x="755" y="800" as="targetPoint" />
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="775" y="790" />
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-136" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="354.57" y="650" as="sourcePoint" />
|
|
||||||
<mxPoint x="354.57" y="520" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-137" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="354.57" y="890" as="sourcePoint" />
|
|
||||||
<mxPoint x="354.57" y="760" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-138" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="1155" y="411" as="sourcePoint" />
|
|
||||||
<mxPoint x="1155" y="381" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-139" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="1155" y="641" as="sourcePoint" />
|
|
||||||
<mxPoint x="1155" y="611" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-140" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="1154.75" y="881" as="sourcePoint" />
|
|
||||||
<mxPoint x="1154.75" y="851" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-141" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;opacity=50;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="755" y="830" as="sourcePoint" />
|
|
||||||
<mxPoint x="754.93" y="312" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-142" value="" style="endArrow=none;html=1;rounded=0;fillColor=#dae8fc;strokeColor=#6c8ebf;strokeWidth=10;opacity=50;gradientColor=#7ea6e0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="165" y="882" as="sourcePoint" />
|
|
||||||
<mxPoint x="165.22" y="292" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-143" value="<font color="#330066">User</font>" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;fillColor=#76608a;strokeColor=#432D57;fontColor=#ffffff;align=center;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="155" y="180" width="21.5" height="43" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-146" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="170" y="280" as="sourcePoint" />
|
|
||||||
<mxPoint x="360" y="280" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-147" value="SAVE" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-146">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-148" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="165.22" y="930" as="sourcePoint" />
|
|
||||||
<mxPoint x="165.22" y="210" as="targetPoint" />
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-149" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="355" y="892" as="sourcePoint" />
|
|
||||||
<mxPoint x="165" y="892" as="targetPoint" />
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="265" y="892" />
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-150" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-149">
|
|
||||||
<mxGeometry relative="1" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-152" value="<h1 style="margin-top: 0px;">UML-диаграмма процесса пакетного редактирования</h1><p>.</p>" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;align=center;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="75" y="50" width="1150" height="70" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-154" value="frame" style="shape=umlFrame;whiteSpace=wrap;html=1;pointerEvents=0;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="120" y="250" width="1120" height="680" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="_dnhcZFeje3u91oS2JwL-156" value="<p style="line-height: 120%;">Время жизни кеша задается внутри проекта</p>" style="shape=note;size=20;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#d0cee2;strokeColor=#56517e;opacity=50;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="760" y="660" width="120" height="100" as="geometry" />
|
|
||||||
</mxCell>
|
|
||||||
</root>
|
|
||||||
</mxGraphModel>
|
|
||||||
</diagram>
|
|
||||||
</mxfile>
|
|
@ -48,7 +48,4 @@ Password: 12345
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Пакетное редактирование (на примере ChangeLog)
|
|
||||||
UML-диаграмма процесса редактирования находится по [ссылке](https://git.ddrilling.ru/on.nemtina/persistence/src/branch/master/DD.Persistence.API/Docs/ChangeLog_actions.drawio.xml)
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using DD.Persistence.Database;
|
using DD.Persistence.Database.Model;
|
||||||
using DD.Persistence.Database.Model;
|
|
||||||
using DD.Persistence.Database.Postgres.Extensions;
|
using DD.Persistence.Database.Postgres.Extensions;
|
||||||
|
using DD.Persistence.Repository;
|
||||||
|
|
||||||
namespace DD.Persistence.API;
|
namespace DD.Persistence.API;
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ public class Startup
|
|||||||
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
// AddRange services to the container.
|
// Add services to the container.
|
||||||
|
|
||||||
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
|
||||||
|
@ -28,6 +28,7 @@ COPY ["DD.Persistence/DD.Persistence.csproj", "DD.Persistence/"]
|
|||||||
COPY ["DD.Persistence.Database/DD.Persistence.Database.csproj", "DD.Persistence.Database/"]
|
COPY ["DD.Persistence.Database/DD.Persistence.Database.csproj", "DD.Persistence.Database/"]
|
||||||
COPY ["DD.Persistence.Database.Postgres/DD.Persistence.Database.Postgres.csproj", "DD.Persistence.Database.Postgres/"]
|
COPY ["DD.Persistence.Database.Postgres/DD.Persistence.Database.Postgres.csproj", "DD.Persistence.Database.Postgres/"]
|
||||||
COPY ["DD.Persistence.Models/DD.Persistence.Models.csproj", "DD.Persistence.Models/"]
|
COPY ["DD.Persistence.Models/DD.Persistence.Models.csproj", "DD.Persistence.Models/"]
|
||||||
|
COPY ["DD.Persistence.Repository/DD.Persistence.Repository.csproj", "DD.Persistence.Repository/"]
|
||||||
|
|
||||||
RUN dotnet restore "./DD.Persistence.App/DD.Persistence.App.csproj"
|
RUN dotnet restore "./DD.Persistence.App/DD.Persistence.App.csproj"
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Host=localhost;Port=5432;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
|
"DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"NeedUseKeyCloak": false,
|
"NeedUseKeyCloak": false,
|
||||||
@ -21,5 +21,5 @@
|
|||||||
"grantType": "password",
|
"grantType": "password",
|
||||||
"http://schemas.xmlsoap.org/ws/2005/05/identity /claims/nameidentifier": "7d9f3574-6574-4ca3-845a-0276eb4aa8f6"
|
"http://schemas.xmlsoap.org/ws/2005/05/identity /claims/nameidentifier": "7d9f3574-6574-4ca3-845a-0276eb4aa8f6"
|
||||||
},
|
},
|
||||||
"ClientUrl": "http://localhost:5000/"
|
"PeristenceClientUrl": "http://localhost:5000/"
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using DD.Persistence.Client.Clients.Base;
|
using DD.Persistence.Client.Clients.Base;
|
||||||
using DD.Persistence.Client.Clients.Interfaces;
|
using DD.Persistence.Client.Clients.Interfaces;
|
||||||
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.Requests;
|
using DD.Persistence.Models.Requests;
|
||||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
using DD.Persistence.Models.ChangeLog;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients;
|
namespace DD.Persistence.Client.Clients;
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@ -19,16 +19,16 @@ public class ChangeLogClient : BaseClient, IChangeLogClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<IDictionary<string, object>> dtos, string comment, CancellationToken token)
|
public async Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await ExecuteGetResponse(
|
var result = await ExecuteGetResponse(
|
||||||
async () => await refitChangeLogClient.ClearAndAddRange(idDiscriminator, dtos, comment, token), token);
|
async () => await refitChangeLogClient.ClearAndAddRange(idDiscriminator, dtos, token), token);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<PaginationContainer<ChangeLogBaseDto>> GetByDate(Guid idDiscriminator, DateTimeOffset moment,
|
public async Task<PaginationContainer<ChangeLogValuesDto>> GetByDate(Guid idDiscriminator, DateTimeOffset moment,
|
||||||
PaginationRequest paginationRequest, CancellationToken token)
|
PaginationRequest paginationRequest, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await ExecuteGetResponse(
|
var result = await ExecuteGetResponse(
|
||||||
@ -47,28 +47,55 @@ public class ChangeLogClient : BaseClient, IChangeLogClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<int> AddRange(Guid idDiscriminator, IEnumerable<IDictionary<string, object>> dtos, string comment, CancellationToken token)
|
public async Task<int> Add(Guid idDiscriminator, ChangeLogValuesDto dto, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await ExecutePostResponse(
|
var result = await ExecutePostResponse(
|
||||||
async () => await refitChangeLogClient.AddRange(idDiscriminator, dtos, comment, token), token);
|
async () => await refitChangeLogClient.Add(idDiscriminator, dto, token), token);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<int> UpdateRange(Guid idDiscriminator, IEnumerable<ChangeLogBaseDto> dtos, string comment, CancellationToken token)
|
public async Task<int> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await ExecutePostResponse(
|
var result = await ExecutePostResponse(
|
||||||
async () => await refitChangeLogClient.UpdateRange(idDiscriminator, dtos, comment, token), token);
|
async () => await refitChangeLogClient.AddRange(idDiscriminator, dtos, token), token);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<int> DeleteRange(Guid idDiscriminator, IEnumerable<Guid> ids, string comment, CancellationToken token)
|
public async Task<int> Update(ChangeLogValuesDto dto, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await ExecutePostResponse(
|
var result = await ExecutePostResponse(
|
||||||
async () => await refitChangeLogClient.DeleteRange(idDiscriminator, ids, comment, token), token);
|
async () => await refitChangeLogClient.Update(dto, token), token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<int> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await ExecutePostResponse(
|
||||||
|
async () => await refitChangeLogClient.UpdateRange(dtos, token), token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<int> Delete(Guid id, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await ExecutePostResponse(
|
||||||
|
async () => await refitChangeLogClient.Delete(id, token), token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<int> DeleteRange(IEnumerable<Guid> ids, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await ExecutePostResponse(
|
||||||
|
async () => await refitChangeLogClient.DeleteRange(ids, token), token);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -82,24 +109,6 @@ public class ChangeLogClient : BaseClient, IChangeLogClient
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<ChangeLogStatisticsDto>> GetStatistics(ChangeLogQuery request, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = await ExecuteGetResponse(
|
|
||||||
async () => await refitChangeLogClient.GetStatistics(request, token), token);
|
|
||||||
|
|
||||||
return result!;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<ChangeLogCommitDto>> GetHistory(ChangeLogQuery request, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = await ExecuteGetResponse(
|
|
||||||
async () => await refitChangeLogClient.GetHistory(request, token), token);
|
|
||||||
|
|
||||||
return result!;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@ -107,5 +116,4 @@ public class ChangeLogClient : BaseClient, IChangeLogClient
|
|||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using DD.Persistence.Models.ChangeLog;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
using DD.Persistence.Models.Requests;
|
using DD.Persistence.Models.Requests;
|
||||||
|
|
||||||
@ -9,35 +9,48 @@ namespace DD.Persistence.Client.Clients.Interfaces;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IChangeLogClient : IDisposable
|
public interface IChangeLogClient : IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить одну запись
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> Add(Guid idDiscriminator, ChangeLogValuesDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавить несколько записей
|
/// Добавить несколько записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idDiscriminator"></param>
|
/// <param name="idDiscriminator"></param>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> AddRange(Guid idDiscriminator, IEnumerable<IDictionary<string, object>> dtos, string comment, CancellationToken token);
|
Task<int> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Импорт с заменой: удаление старых строк и добавление новых
|
/// Импорт с заменой: удаление старых строк и добавление новых
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idDiscriminator"></param>
|
/// <param name="idDiscriminator"></param>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<IDictionary<string, object>> dtos, string comment, CancellationToken token);
|
Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Удалить одну запись
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> Delete(Guid id, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Удалить несколько записей
|
/// Удалить несколько записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="ids"></param>
|
/// <param name="ids"></param>
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> DeleteRange(Guid idDiscriminator, IEnumerable<Guid> ids, string comment, CancellationToken token);
|
Task<int> DeleteRange(IEnumerable<Guid> ids, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение актуальных данных на определенную дату (с пагинацией)
|
/// Получение актуальных данных на определенную дату (с пагинацией)
|
||||||
@ -47,7 +60,7 @@ public interface IChangeLogClient : IDisposable
|
|||||||
/// <param name="paginationRequest"></param>
|
/// <param name="paginationRequest"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<PaginationContainer<ChangeLogBaseDto>> GetByDate(Guid idDiscriminator, DateTimeOffset moment, PaginationRequest paginationRequest, CancellationToken token);
|
Task<PaginationContainer<ChangeLogValuesDto>> GetByDate(Guid idDiscriminator, DateTimeOffset moment, PaginationRequest paginationRequest, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение исторических данных за определенный период времени
|
/// Получение исторических данных за определенный период времени
|
||||||
@ -67,29 +80,19 @@ public interface IChangeLogClient : IDisposable
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token);
|
Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обновить одну запись
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> Update(ChangeLogValuesDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновить несколько записей
|
/// Обновить несколько записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> UpdateRange(Guid idDiscriminator, IEnumerable<ChangeLogBaseDto> dtos, string comment, CancellationToken token);
|
Task<int> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение статистики журнала изменений
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<ChangeLogStatisticsDto>> GetStatistics(ChangeLogQuery request, CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение истории по журналу изменений
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<ChangeLogCommitDto>> GetHistory(ChangeLogQuery request, CancellationToken token);
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients.Interfaces;
|
namespace DD.Persistence.Client.Clients.Interfaces;
|
||||||
@ -24,19 +24,28 @@ public interface ITimestampedValuesClient : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="discriminatorIds">Набор дискриминаторов (идентификаторов)</param>
|
/// <param name="discriminatorIds">Набор дискриминаторов (идентификаторов)</param>
|
||||||
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
/// <param name="timestampBegin">Фильтр позднее даты</param>
|
||||||
/// <param name="filterTree">Кастомный фильтр по набору значений</param>
|
|
||||||
/// <param name="columnNames">Фильтр свойств набора</param>
|
/// <param name="columnNames">Фильтр свойств набора</param>
|
||||||
/// <param name="skip"></param>
|
/// <param name="skip"></param>
|
||||||
/// <param name="take"></param>
|
/// <param name="take"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds,
|
Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds,
|
||||||
DateTimeOffset? timestampBegin,
|
DateTimeOffset? timestampBegin,
|
||||||
string? filterTree,
|
|
||||||
IEnumerable<string>? columnNames,
|
IEnumerable<string>? columnNames,
|
||||||
int skip,
|
int skip,
|
||||||
int take,
|
int take,
|
||||||
CancellationToken token);
|
CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить данные с фильтрацией для нескольких систем. Значение фильтра null - отключен
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="discriminatorId"></param>
|
||||||
|
/// <param name="geTimestamp"></param>
|
||||||
|
/// <param name="columnNames">Фильтр свойств набора</param>
|
||||||
|
/// <param name="skip"></param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить данные, начиная с заданной отметки времени
|
/// Получить данные, начиная с заданной отметки времени
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -85,6 +94,13 @@ public interface ITimestampedValuesClient : IDisposable
|
|||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token);
|
Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IEnumerable<T>> GetLast<T>(Guid idDiscriminator, int take, CancellationToken token);
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using DD.Persistence.Models.ChangeLog;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
using DD.Persistence.Models.Requests;
|
using DD.Persistence.Models.Requests;
|
||||||
using Refit;
|
using Refit;
|
||||||
@ -16,13 +16,13 @@ public interface IRefitChangeLogClient : IRefitClient, IDisposable
|
|||||||
/// Импорт с заменой: удаление старых строк и добавление новых
|
/// Импорт с заменой: удаление старых строк и добавление новых
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Post($"{BaseRoute}/replace/{{idDiscriminator}}")]
|
[Post($"{BaseRoute}/replace/{{idDiscriminator}}")]
|
||||||
Task<IApiResponse<int>> ClearAndAddRange(Guid idDiscriminator, IEnumerable<IDictionary<string, object>> dtos, string comment, CancellationToken token);
|
Task<IApiResponse<int>> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение актуальных данных на определенную дату (с пагинацией)
|
/// Получение актуальных данных на определенную дату (с пагинацией)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Get($"{BaseRoute}/moment/{{idDiscriminator}}")]
|
[Get($"{BaseRoute}/moment/{{idDiscriminator}}")]
|
||||||
Task<IApiResponse<PaginationContainer<ChangeLogBaseDto>>> GetByDate(
|
Task<IApiResponse<PaginationContainer<ChangeLogValuesDto>>> GetByDate(
|
||||||
Guid idDiscriminator,
|
Guid idDiscriminator,
|
||||||
DateTimeOffset moment,
|
DateTimeOffset moment,
|
||||||
[Query] PaginationRequest paginationRequest,
|
[Query] PaginationRequest paginationRequest,
|
||||||
@ -35,26 +35,40 @@ public interface IRefitChangeLogClient : IRefitClient, IDisposable
|
|||||||
Task<IApiResponse<IEnumerable<ChangeLogDto>>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
|
Task<IApiResponse<IEnumerable<ChangeLogDto>>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавить несколько записей
|
/// Добавить одну запись
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Post($"{BaseRoute}/{{idDiscriminator}}")]
|
[Post($"{BaseRoute}/{{idDiscriminator}}")]
|
||||||
Task<IApiResponse<int>> AddRange(
|
Task<IApiResponse<int>> Add(Guid idDiscriminator, ChangeLogValuesDto dto, CancellationToken token);
|
||||||
Guid idDiscriminator,
|
|
||||||
IEnumerable<IDictionary<string, object>> dtos,
|
/// <summary>
|
||||||
string comment,
|
/// Добавить несколько записей
|
||||||
CancellationToken token);
|
/// </summary>
|
||||||
|
[Post($"{BaseRoute}/range/{{idDiscriminator}}")]
|
||||||
|
Task<IApiResponse<int>> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обновить одну запись
|
||||||
|
/// </summary>
|
||||||
|
[Put($"{BaseRoute}")]
|
||||||
|
Task<IApiResponse<int>> Update(ChangeLogValuesDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновить несколько записей
|
/// Обновить несколько записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Put($"{BaseRoute}")]
|
[Put($"{BaseRoute}/range")]
|
||||||
Task<IApiResponse<int>> UpdateRange(Guid idDiscriminator, IEnumerable<ChangeLogBaseDto> dtos, string comment, CancellationToken token);
|
Task<IApiResponse<int>> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Удалить одну запись
|
||||||
|
/// </summary>
|
||||||
|
[Delete($"{BaseRoute}")]
|
||||||
|
Task<IApiResponse<int>> Delete(Guid id, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Удалить несколько записей
|
/// Удалить несколько записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Delete($"{BaseRoute}")]
|
[Delete($"{BaseRoute}/range")]
|
||||||
Task<IApiResponse<int>> DeleteRange(Guid idDiscriminator, [Body] IEnumerable<Guid> ids, string comment, CancellationToken token);
|
Task<IApiResponse<int>> DeleteRange([Body] IEnumerable<Guid> ids, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
|
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
|
||||||
@ -62,21 +76,4 @@ public interface IRefitChangeLogClient : IRefitClient, IDisposable
|
|||||||
[Get($"{BaseRoute}/datesRange/{{idDiscriminator}}")]
|
[Get($"{BaseRoute}/datesRange/{{idDiscriminator}}")]
|
||||||
Task<IApiResponse<DatesRangeDto?>> GetDatesRange(Guid idDiscriminator, CancellationToken token);
|
Task<IApiResponse<DatesRangeDto?>> GetDatesRange(Guid idDiscriminator, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение статистики журнала изменений
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Get($"{BaseRoute}/statistics")]
|
|
||||||
Task<IApiResponse<IEnumerable<ChangeLogStatisticsDto>>> GetStatistics(ChangeLogQuery request, CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получение истории по журналу изменений
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Get($"{BaseRoute}/history")]
|
|
||||||
Task<IApiResponse<IEnumerable<ChangeLogCommitDto>>> GetHistory(ChangeLogQuery request, CancellationToken token);
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable
|
|||||||
[Get($"{baseUrl}")]
|
[Get($"{baseUrl}")]
|
||||||
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> Get([Query(CollectionFormat.Multi)] IEnumerable<Guid> discriminatorIds,
|
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> Get([Query(CollectionFormat.Multi)] IEnumerable<Guid> discriminatorIds,
|
||||||
DateTimeOffset? timestampBegin,
|
DateTimeOffset? timestampBegin,
|
||||||
[Query] string? filterTree,
|
|
||||||
[Query(CollectionFormat.Multi)] IEnumerable<string>? columnNames,
|
[Query(CollectionFormat.Multi)] IEnumerable<string>? columnNames,
|
||||||
int skip,
|
int skip,
|
||||||
int take,
|
int take,
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
namespace DD.Persistence.Client.Clients.Mapping.Abstractions;
|
|
||||||
internal interface IMapperStorage
|
|
||||||
{
|
|
||||||
TimestampedSetMapper GetMapper<T>(Guid idDiscriminator);
|
|
||||||
TimestampedSetMapper? GetMapper(Guid idDiscriminator);
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
using DD.Persistence.Client.Clients.Interfaces;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients.Mapping.Abstractions;
|
|
||||||
public interface ISetpointMappingClient : ISetpointClient
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
using DD.Persistence.Client.Clients.Interfaces;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients.Mapping.Abstractions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Маппинг - обертка для клиента по работе с данными
|
|
||||||
/// </summary>
|
|
||||||
public interface ITimestampedMappingClient : ITimestampedValuesClient
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Получить данные с преобразованием к заданному типу
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="discriminatorId"></param>
|
|
||||||
/// <param name="geTimestamp"></param>
|
|
||||||
/// <param name="columnNames">Фильтр свойств набора</param>
|
|
||||||
/// <param name="filterTree"></param>
|
|
||||||
/// <param name="skip"></param>
|
|
||||||
/// <param name="take"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
Task<IEnumerable<T>> GetMapped<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить набор данных, преобразованных к соответствующим типам из заданного конфига
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="discriminatorIds"></param>
|
|
||||||
/// <param name="timestampBegin"></param>
|
|
||||||
/// <param name="filterTree"></param>
|
|
||||||
/// <param name="columnNames"></param>
|
|
||||||
/// <param name="skip"></param>
|
|
||||||
/// <param name="take"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IDictionary<Guid, IEnumerable<object>>> GetMultiMapped(IEnumerable<Guid> discriminatorIds, DateTimeOffset? timestampBegin, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить данные с конца с преобразованием к заданному типу
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
|
||||||
/// <param name="take"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<T>> GetLastMapped<T>(Guid idDiscriminator, int take, CancellationToken token);
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
using DD.Persistence.Client.Clients.Interfaces;
|
|
||||||
using DD.Persistence.Client.Clients.Mapping.Abstractions;
|
|
||||||
using DD.Persistence.Models;
|
|
||||||
using DD.Persistence.Models.Common;
|
|
||||||
using DD.Persistence.Models.Configurations;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients.Mapping.Clients;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public class SetpointMappingClient : ISetpointMappingClient
|
|
||||||
{
|
|
||||||
private readonly ISetpointClient setpointClient;
|
|
||||||
private readonly MappingConfig mappingConfigs;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public SetpointMappingClient(ISetpointClient setpointClient, MappingConfig mappingConfigs)
|
|
||||||
{
|
|
||||||
this.setpointClient = setpointClient;
|
|
||||||
this.mappingConfigs = mappingConfigs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token)
|
|
||||||
=> (await setpointClient.GetCurrent(setpointKeys, token))
|
|
||||||
.Select(x => new SetpointValueDto
|
|
||||||
{
|
|
||||||
Key = x.Key,
|
|
||||||
Value = DeserializeValue(x.Key, (JsonElement)x.Value)
|
|
||||||
});
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<Dictionary<Guid, object>> GetCurrentDictionary(IEnumerable<Guid> setpointConfigs, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = (await setpointClient.GetCurrent(setpointConfigs, token))
|
|
||||||
.ToDictionary(x => x.Key, x => DeserializeValue(x.Key, (JsonElement)x.Value));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = await setpointClient.GetHistory(setpointKeys, historyMoment, token);
|
|
||||||
|
|
||||||
foreach (var dto in result)
|
|
||||||
dto.Value = DeserializeValue(dto.Key, (JsonElement)dto.Value);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = await setpointClient.GetLog(setpointKeys, token);
|
|
||||||
|
|
||||||
foreach (var item in result)
|
|
||||||
DeserializeList(result[item.Key]);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<SetpointLogDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = await setpointClient.GetPart(dateBegin, take, token);
|
|
||||||
|
|
||||||
DeserializeList(result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task Add(Guid setpointKey, object newValue, CancellationToken token)
|
|
||||||
=> await setpointClient.Add(setpointKey, newValue, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token)
|
|
||||||
=> await setpointClient.GetDatesRangeAsync(token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
setpointClient.Dispose();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private object DeserializeValue(Guid key, JsonElement value)
|
|
||||||
{
|
|
||||||
if (mappingConfigs.TryGetValue(key, out var type))
|
|
||||||
return value.Deserialize(type)!;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
private void DeserializeList(IEnumerable<SetpointLogDto>? result)
|
|
||||||
{
|
|
||||||
if (result is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var log in result)
|
|
||||||
log.Value = DeserializeValue(log.Key, (JsonElement)log.Value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
using DD.Persistence.Client.Clients.Interfaces;
|
|
||||||
using DD.Persistence.Client.Clients.Mapping.Abstractions;
|
|
||||||
using DD.Persistence.Models;
|
|
||||||
using DD.Persistence.Models.Common;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients.Mapping.Clients;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
internal class TimestampedMappingClient : ITimestampedMappingClient
|
|
||||||
{
|
|
||||||
private readonly ITimestampedValuesClient client;
|
|
||||||
private readonly IMapperStorage mapperStorage;
|
|
||||||
|
|
||||||
public TimestampedMappingClient(ITimestampedValuesClient client, IMapperStorage mapperStorage)
|
|
||||||
{
|
|
||||||
this.client = client;
|
|
||||||
this.mapperStorage = mapperStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<T>> GetMapped<T>(Guid discriminatorId, DateTimeOffset? geTimestamp,
|
|
||||||
string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
|
||||||
{
|
|
||||||
var data = await Get([discriminatorId], geTimestamp, filterTree, columnNames, skip, take, token);
|
|
||||||
var mapper = mapperStorage.GetMapper<T>(discriminatorId);
|
|
||||||
|
|
||||||
var mappedDtos = data.Select(mapper.DeserializeTimeStampedData).OfType<T>();
|
|
||||||
|
|
||||||
return mappedDtos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<T>> GetLastMapped<T>(Guid idDiscriminator, int take, CancellationToken token)
|
|
||||||
{
|
|
||||||
var data = await GetLast(idDiscriminator, take, token);
|
|
||||||
var mapper = mapperStorage.GetMapper<T>(idDiscriminator);
|
|
||||||
|
|
||||||
var mappedDtos = data.Select(mapper.DeserializeTimeStampedData).OfType<T>();
|
|
||||||
|
|
||||||
return mappedDtos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IDictionary<Guid, IEnumerable<object>>> GetMultiMapped(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp,
|
|
||||||
string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
|
||||||
{
|
|
||||||
var data = await client.Get(discriminatorIds, geTimestamp, filterTree, columnNames, skip, take, token);
|
|
||||||
|
|
||||||
var result = discriminatorIds
|
|
||||||
.ToDictionary(discriminatorId => discriminatorId, discriminatorId =>
|
|
||||||
{
|
|
||||||
var mapper = mapperStorage.GetMapper(discriminatorId);
|
|
||||||
|
|
||||||
ArgumentNullException.ThrowIfNull(mapper);
|
|
||||||
|
|
||||||
var mappedDtos = data
|
|
||||||
.Where(e => e.DiscriminatorId == discriminatorId)
|
|
||||||
.Select(mapper.DeserializeTimeStampedData);
|
|
||||||
|
|
||||||
return mappedDtos;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
|
|
||||||
=> await client.AddRange(discriminatorId, dtos, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<int> Count(Guid discriminatorId, CancellationToken token)
|
|
||||||
=> await client.Count(discriminatorId, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? timestampBegin,
|
|
||||||
string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
|
||||||
=> await client.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
|
|
||||||
=> await client.GetDatesRange(discriminatorId, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<TimestampedValuesDto>> GetFirst(Guid discriminatorId, int take, CancellationToken token)
|
|
||||||
=> await client.GetFirst(discriminatorId, take, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
|
||||||
=> await client.GetGtDate(discriminatorId, timestampBegin, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<TimestampedValuesDto>> GetLast(Guid discriminatorId, int take, CancellationToken token)
|
|
||||||
=> await client.GetLast(discriminatorId, take, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<IEnumerable<TimestampedValuesDto>> GetResampledData(Guid discriminatorId, DateTimeOffset timestampBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default)
|
|
||||||
=> await client.GetResampledData(discriminatorId, timestampBegin, intervalSec, approxPointsCount, token);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
client.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
using DD.Persistence.Client.Clients.Mapping.Abstractions;
|
|
||||||
using DD.Persistence.Models.Configurations;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients.Mapping;
|
|
||||||
internal class MapperStorage : IMapperStorage
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<Guid, TimestampedSetMapper> mapperCache = new();
|
|
||||||
private readonly MappingConfig mappingConfigs;
|
|
||||||
private readonly ILogger<TimestampedSetMapper> logger;
|
|
||||||
|
|
||||||
public MapperStorage(MappingConfig mappingConfigs, ILogger<TimestampedSetMapper> logger)
|
|
||||||
{
|
|
||||||
this.mappingConfigs = mappingConfigs;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public TimestampedSetMapper? GetMapper(Guid idDiscriminator)
|
|
||||||
{
|
|
||||||
if (mappingConfigs.TryGetValue(idDiscriminator, out var type))
|
|
||||||
return mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper(idDiscriminator, type, logger));
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimestampedSetMapper GetMapper<T>(Guid idDiscriminator)
|
|
||||||
{
|
|
||||||
return mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper(idDiscriminator, typeof(T), logger));
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,9 @@ using DD.Persistence.Client.Clients.Base;
|
|||||||
using DD.Persistence.Client.Clients.Interfaces;
|
using DD.Persistence.Client.Clients.Interfaces;
|
||||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Globalization;
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients;
|
namespace DD.Persistence.Client.Clients;
|
||||||
@ -10,12 +13,15 @@ namespace DD.Persistence.Client.Clients;
|
|||||||
public class SetpointClient : BaseClient, ISetpointClient
|
public class SetpointClient : BaseClient, ISetpointClient
|
||||||
{
|
{
|
||||||
private readonly IRefitSetpointClient refitSetpointClient;
|
private readonly IRefitSetpointClient refitSetpointClient;
|
||||||
|
private readonly ISetpointConfigStorage setpointConfigStorage;
|
||||||
|
|
||||||
public SetpointClient(
|
public SetpointClient(
|
||||||
IRefitClientFactory<IRefitSetpointClient> refitSetpointClientFactory,
|
IRefitClientFactory<IRefitSetpointClient> refitSetpointClientFactory,
|
||||||
|
ISetpointConfigStorage setpointConfigStorage,
|
||||||
ILogger<SetpointClient> logger) : base(logger)
|
ILogger<SetpointClient> logger) : base(logger)
|
||||||
{
|
{
|
||||||
this.refitSetpointClient = refitSetpointClientFactory.Create();
|
this.refitSetpointClient = refitSetpointClientFactory.Create();
|
||||||
|
this.setpointConfigStorage = setpointConfigStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token)
|
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token)
|
||||||
@ -25,7 +31,7 @@ public class SetpointClient : BaseClient, ISetpointClient
|
|||||||
|
|
||||||
return result!.Select(x => new SetpointValueDto {
|
return result!.Select(x => new SetpointValueDto {
|
||||||
Key = x.Key,
|
Key = x.Key,
|
||||||
Value = x.Value
|
Value = DeserializeValue(x.Key, x.Value)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +43,7 @@ public class SetpointClient : BaseClient, ISetpointClient
|
|||||||
async () => await refitSetpointClient.GetCurrent(setpointConfigs, token), token);
|
async () => await refitSetpointClient.GetCurrent(setpointConfigs, token), token);
|
||||||
|
|
||||||
|
|
||||||
return result!.ToDictionary(x => x.Key,x => (object)x.Value);
|
return result!.ToDictionary(x => x.Key,x => DeserializeValue(x.Key,x.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
|
public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
|
||||||
@ -45,6 +51,9 @@ public class SetpointClient : BaseClient, ISetpointClient
|
|||||||
var result = await ExecuteGetResponse(
|
var result = await ExecuteGetResponse(
|
||||||
async () => await refitSetpointClient.GetHistory(setpointKeys, historyMoment, token), token);
|
async () => await refitSetpointClient.GetHistory(setpointKeys, historyMoment, token), token);
|
||||||
|
|
||||||
|
foreach(var dto in result)
|
||||||
|
dto.Value = DeserializeValue(dto.Key, (JsonElement)dto.Value);
|
||||||
|
|
||||||
|
|
||||||
return result!;
|
return result!;
|
||||||
}
|
}
|
||||||
@ -54,6 +63,9 @@ public class SetpointClient : BaseClient, ISetpointClient
|
|||||||
var result = await ExecuteGetResponse(
|
var result = await ExecuteGetResponse(
|
||||||
async () => await refitSetpointClient.GetLog(setpointKeys, token), token);
|
async () => await refitSetpointClient.GetLog(setpointKeys, token), token);
|
||||||
|
|
||||||
|
foreach(var item in result)
|
||||||
|
DeserializeList(result[item.Key]);
|
||||||
|
|
||||||
return result!;
|
return result!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +82,8 @@ public class SetpointClient : BaseClient, ISetpointClient
|
|||||||
var result = await ExecuteGetResponse(
|
var result = await ExecuteGetResponse(
|
||||||
async () => await refitSetpointClient.GetPart(dateBegin, take, token), token);
|
async () => await refitSetpointClient.GetPart(dateBegin, take, token), token);
|
||||||
|
|
||||||
|
DeserializeList(result);
|
||||||
|
|
||||||
return result!;
|
return result!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,4 +101,21 @@ public class SetpointClient : BaseClient, ISetpointClient
|
|||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private object DeserializeValue(Guid key, JsonElement value)
|
||||||
|
{
|
||||||
|
if (setpointConfigStorage.TryGetType(key, out var type))
|
||||||
|
return value.Deserialize(type)!;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
private void DeserializeList(IEnumerable<SetpointLogDto>? result)
|
||||||
|
{
|
||||||
|
foreach (var log in result)
|
||||||
|
log.Value = DeserializeValue(log.Key, (JsonElement)log.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using DD.Persistence.Client.Clients.Base;
|
using DD.Persistence.Client.Clients.Base;
|
||||||
using DD.Persistence.Client.Clients.Interfaces;
|
using DD.Persistence.Client.Clients.Interfaces;
|
||||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
@ -19,7 +19,8 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient
|
|||||||
this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create();
|
this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
private readonly ConcurrentDictionary<Guid, TimestampedSetMapperBase> mapperCache = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> sets, CancellationToken token)
|
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> sets, CancellationToken token)
|
||||||
@ -31,13 +32,22 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await ExecuteGetResponse(
|
var result = await ExecuteGetResponse(
|
||||||
async () => await refitTimestampedSetClient.Get(discriminatorIds, geTimestamp, filterTree, columnNames, skip, take, token), token);
|
async () => await refitTimestampedSetClient.Get(discriminatorIds, geTimestamp, columnNames, skip, take, token), token);
|
||||||
return result!;
|
return result!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
|
||||||
|
{
|
||||||
|
var data = await Get([discriminatorId], geTimestamp, columnNames, skip, take, token);
|
||||||
|
var mapper = GetMapper<T>(discriminatorId);
|
||||||
|
|
||||||
|
return data.Select(mapper.DeserializeTimeStampedData);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
||||||
{
|
{
|
||||||
@ -92,7 +102,20 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<IEnumerable<T>> GetLast<T>(Guid idDiscriminator, int take, CancellationToken token)
|
||||||
|
{
|
||||||
|
var data = await GetLast(idDiscriminator, take, token);
|
||||||
|
var mapper = GetMapper<T>(idDiscriminator);
|
||||||
|
|
||||||
|
return data.Select(mapper.DeserializeTimeStampedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
private TimestampedSetMapper<T> GetMapper<T>(Guid idDiscriminator)
|
||||||
|
{
|
||||||
|
return (TimestampedSetMapper<T>)mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper<T>(idDiscriminator));
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DD.Persistence.Client.CustomExceptions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ошибка, которая выбрасывается в случае отсутствия необходимого свойства в appsettings.json
|
||||||
|
/// </summary>
|
||||||
|
public class AppSettingsPropertyNotFoundException : Exception
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
public AppSettingsPropertyNotFoundException(string message)
|
||||||
|
: base(message) { }
|
||||||
|
}
|
@ -52,7 +52,6 @@
|
|||||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
|
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
|
||||||
<PackageReference Include="Refit" Version="8.0.0" />
|
<PackageReference Include="Refit" Version="8.0.0" />
|
||||||
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
|
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
|
||||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="9.0.0" />
|
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
using DD.Persistence.Client.Clients;
|
using DD.Persistence.Client.Clients;
|
||||||
using DD.Persistence.Client.Clients.Interfaces;
|
using DD.Persistence.Client.Clients.Interfaces;
|
||||||
using DD.Persistence.Client.Clients.Mapping;
|
|
||||||
using DD.Persistence.Client.Clients.Mapping.Abstractions;
|
|
||||||
using DD.Persistence.Client.Clients.Mapping.Clients;
|
|
||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.Configurations;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace DD.Persistence.Client;
|
namespace DD.Persistence.Client;
|
||||||
@ -19,7 +15,7 @@ public static class DependencyInjection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services"></param>
|
/// <param name="services"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IServiceCollection AddPersistenceClients(this IServiceCollection services)
|
public static IServiceCollection AddPersistenceClients(this IServiceCollection services, Dictionary<Guid, Type>? setpointTypeConfigs = null)
|
||||||
{
|
{
|
||||||
services.AddTransient(typeof(IRefitClientFactory<>), typeof(RefitClientFactory<>));
|
services.AddTransient(typeof(IRefitClientFactory<>), typeof(RefitClientFactory<>));
|
||||||
services.AddTransient<IChangeLogClient, ChangeLogClient>();
|
services.AddTransient<IChangeLogClient, ChangeLogClient>();
|
||||||
@ -29,17 +25,10 @@ public static class DependencyInjection
|
|||||||
services.AddTransient<ITimestampedValuesClient, TimestampedValuesClient>();
|
services.AddTransient<ITimestampedValuesClient, TimestampedValuesClient>();
|
||||||
services.AddTransient<IWitsDataClient, WitsDataClient>();
|
services.AddTransient<IWitsDataClient, WitsDataClient>();
|
||||||
|
|
||||||
return services;
|
services.AddSingleton<ISetpointConfigStorage, SetpointConfigStorage>(provider =>
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static IServiceCollection AddPersistenceMapping(this IServiceCollection services, MappingConfig mappingConfigs)
|
|
||||||
{
|
{
|
||||||
services.AddSingleton(mappingConfigs);
|
return new SetpointConfigStorage(setpointTypeConfigs);
|
||||||
services.AddSingleton<IMapperStorage, MapperStorage>();
|
});
|
||||||
|
|
||||||
services.AddTransient<ISetpointMappingClient, SetpointMappingClient>();
|
|
||||||
services.AddTransient<ITimestampedMappingClient, TimestampedMappingClient>();
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
DD.Persistence.Client/ISetpointConfigStorage.cs
Normal file
11
DD.Persistence.Client/ISetpointConfigStorage.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DD.Persistence.Client;
|
||||||
|
public interface ISetpointConfigStorage
|
||||||
|
{
|
||||||
|
bool TryGetType(Guid id, out Type type);
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||||
using DD.Persistence.Client.Helpers;
|
using DD.Persistence.Client.CustomExceptions;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Refit;
|
using Refit;
|
||||||
using System.Configuration;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace DD.Persistence.Client;
|
namespace DD.Persistence.Client;
|
||||||
@ -23,10 +22,10 @@ public class RefitClientFactory<T> : IRefitClientFactory<T> where T : IRefitClie
|
|||||||
//this.client = factory.CreateClient();
|
//this.client = factory.CreateClient();
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
var baseUrl = configuration.GetSection("ClientUrl").Get<string>();
|
var baseUrl = configuration.GetSection("PeristenceClientUrl").Get<string>();
|
||||||
if (String.IsNullOrEmpty(baseUrl))
|
if (String.IsNullOrEmpty(baseUrl))
|
||||||
{
|
{
|
||||||
var exception = new SettingsPropertyNotFoundException("В настройках конфигурации не указан адрес Persistence сервиса.");
|
var exception = new AppSettingsPropertyNotFoundException("В настройках конфигурации не указан адрес Persistence сервиса.");
|
||||||
|
|
||||||
logger.LogError(exception.Message);
|
logger.LogError(exception.Message);
|
||||||
|
|
||||||
|
20
DD.Persistence.Client/SetpointConfigStorage.cs
Normal file
20
DD.Persistence.Client/SetpointConfigStorage.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace DD.Persistence.Client;
|
||||||
|
internal class SetpointConfigStorage : ISetpointConfigStorage
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Guid, Type> setpointTypeConfigs;
|
||||||
|
|
||||||
|
public SetpointConfigStorage(Dictionary<Guid, Type>? setpointTypeConfigs)
|
||||||
|
{
|
||||||
|
this.setpointTypeConfigs = setpointTypeConfigs?? new Dictionary<Guid, Type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetType(Guid id, out Type type)
|
||||||
|
{
|
||||||
|
return setpointTypeConfigs.TryGetValue(id, out type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddOrReplace(Guid id, Type type)
|
||||||
|
{
|
||||||
|
setpointTypeConfigs[id] = type;
|
||||||
|
}
|
||||||
|
}
|
@ -1,62 +1,65 @@
|
|||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace DD.Persistence.Client.Clients.Mapping;
|
namespace DD.Persistence.Client;
|
||||||
|
|
||||||
internal class TimestampedSetMapper
|
internal abstract class TimestampedSetMapperBase
|
||||||
{
|
{
|
||||||
private readonly Type entityType;
|
public abstract object Map(TimestampedValuesDto data);
|
||||||
private readonly ILogger<TimestampedSetMapper> logger;
|
|
||||||
|
|
||||||
|
}
|
||||||
|
internal class TimestampedSetMapper<T> : TimestampedSetMapperBase
|
||||||
|
{
|
||||||
|
private readonly Type entityType = typeof(T);
|
||||||
public Guid IdDiscriminator { get; }
|
public Guid IdDiscriminator { get; }
|
||||||
private readonly ConcurrentDictionary<string, PropertyInfo?> PropertyCache = new();
|
private readonly ConcurrentDictionary<string, PropertyInfo?> PropertyCache = new();
|
||||||
|
|
||||||
public TimestampedSetMapper(Guid idDiscriminator, Type entityType, ILogger<TimestampedSetMapper> logger)
|
public TimestampedSetMapper(Guid idDiscriminator)
|
||||||
{
|
{
|
||||||
IdDiscriminator = idDiscriminator;
|
IdDiscriminator = idDiscriminator;
|
||||||
this.entityType = entityType;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object DeserializeTimeStampedData(TimestampedValuesDto data)
|
public override object Map(TimestampedValuesDto data)
|
||||||
{
|
{
|
||||||
|
return DeserializeTimeStampedData(data)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T DeserializeTimeStampedData(TimestampedValuesDto data)
|
||||||
|
{
|
||||||
|
|
||||||
if (entityType.IsValueType)
|
if (entityType.IsValueType)
|
||||||
return MapStruct(data);
|
return MapStruct(data);
|
||||||
|
else
|
||||||
return MapClass(data);
|
return MapClass(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private object MapClass(TimestampedValuesDto data)
|
private T MapClass(TimestampedValuesDto data)
|
||||||
{
|
{
|
||||||
var entity = RuntimeHelpers.GetUninitializedObject(entityType);
|
var entity = (T)RuntimeHelpers.GetUninitializedObject(typeof(T));
|
||||||
foreach (var (propertyName, value) in data.Values)
|
foreach (var (propertyName, value) in data.Values)
|
||||||
{
|
{
|
||||||
if (value is JsonElement jsonElement)
|
if (value is JsonElement jsonElement)
|
||||||
SetPropertyValueFromJson(ref entity, propertyName, jsonElement);
|
SetPropertyValueFromJson(ref entity, propertyName, jsonElement);
|
||||||
}
|
}
|
||||||
SetPropertyValue(ref entity, nameof(TimestampedValuesDto.Timestamp), data.Timestamp);
|
SetPropertyValue(ref entity, "Timestamp", data.Timestamp);
|
||||||
SetPropertyValue(ref entity, nameof(TimestampedValuesDto.DiscriminatorId), data.DiscriminatorId);
|
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private object MapStruct(TimestampedValuesDto data)
|
private T MapStruct(TimestampedValuesDto data)
|
||||||
{
|
{
|
||||||
var entity = Activator.CreateInstance(entityType);
|
var entity = Activator.CreateInstance<T>();
|
||||||
object boxedEntity = entity!;
|
object boxedEntity = entity!;
|
||||||
foreach (var (propertyName, value) in data.Values)
|
foreach (var (propertyName, value) in data.Values)
|
||||||
{
|
{
|
||||||
if (value is JsonElement jsonElement)
|
if (value is JsonElement jsonElement)
|
||||||
SetPropertyValueForStructFromJson(ref boxedEntity, propertyName, jsonElement);
|
SetPropertyValueForStructFromJson(ref boxedEntity, propertyName, jsonElement);
|
||||||
}
|
}
|
||||||
SetPropertyValueForStruct(ref boxedEntity, nameof(TimestampedValuesDto.Timestamp), data.Timestamp);
|
SetPropertyValueForStruct(ref boxedEntity, "Timestamp", data.Timestamp);
|
||||||
SetPropertyValueForStruct(ref boxedEntity, nameof(TimestampedValuesDto.DiscriminatorId), data.DiscriminatorId);
|
|
||||||
|
|
||||||
return boxedEntity;
|
return (T)boxedEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetPropertyValueForStructFromJson(ref object entity, string propertyName, JsonElement element)
|
private void SetPropertyValueForStructFromJson(ref object entity, string propertyName, JsonElement element)
|
||||||
@ -72,7 +75,6 @@ internal class TimestampedSetMapper
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError(ex.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void SetPropertyValueForStruct(ref object entity, string propertyName, object value)
|
private void SetPropertyValueForStruct(ref object entity, string propertyName, object value)
|
||||||
@ -88,12 +90,11 @@ internal class TimestampedSetMapper
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError(ex.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void SetPropertyValueFromJson(ref object entity, string propertyName, JsonElement jsonElement)
|
private void SetPropertyValueFromJson(ref T entity, string propertyName, JsonElement jsonElement)
|
||||||
{
|
{
|
||||||
var property = GetPropertyInfo(propertyName);
|
var property = GetPropertyInfo(propertyName);
|
||||||
|
|
||||||
@ -107,11 +108,11 @@ internal class TimestampedSetMapper
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError(ex.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetPropertyValue(ref object entity, string propertyName, object value)
|
private void SetPropertyValue(ref T entity, string propertyName, object value)
|
||||||
{
|
{
|
||||||
var property = GetPropertyInfo(propertyName);
|
var property = GetPropertyInfo(propertyName);
|
||||||
if (property is null)
|
if (property is null)
|
||||||
@ -124,7 +125,6 @@ internal class TimestampedSetMapper
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError(ex.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,6 @@
|
|||||||
using DD.Persistence.Database.Entity;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using DD.Persistence.Database.Postgres.Repositories;
|
|
||||||
using DD.Persistence.Database.Postgres.RepositoriesCached;
|
|
||||||
using DD.Persistence.Models;
|
|
||||||
using DD.Persistence.Repositories;
|
|
||||||
using Mapster;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Model;
|
namespace DD.Persistence.Database.Model;
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace DD.Persistence.Database.Postgres.Migrations
|
namespace DD.Persistence.Database.Postgres.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PersistencePostgresContext))]
|
[DbContext(typeof(PersistencePostgresContext))]
|
||||||
[Migration("20250228042803_Innit")]
|
[Migration("20250203061429_Init")]
|
||||||
partial class Innit
|
partial class Init
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@ -33,17 +33,29 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Ключ записи");
|
.HasComment("Ключ записи");
|
||||||
|
|
||||||
b.Property<Guid>("IdCreatedCommit")
|
b.Property<DateTimeOffset>("Creation")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("Дата создания записи");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdAuthor")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Id коммита на создание записи");
|
.HasComment("Автор изменения");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdDiscriminator")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Дискриминатор таблицы");
|
||||||
|
|
||||||
|
b.Property<Guid?>("IdEditor")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Редактор");
|
||||||
|
|
||||||
b.Property<Guid?>("IdNext")
|
b.Property<Guid?>("IdNext")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Id заменяющей записи");
|
.HasComment("Id заменяющей записи");
|
||||||
|
|
||||||
b.Property<Guid?>("IdObsoletedCommit")
|
b.Property<DateTimeOffset?>("Obsolete")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasComment("Id коммита на устаревание записи");
|
.HasComment("Дата устаревания (например при удалении)");
|
||||||
|
|
||||||
b.Property<string>("Value")
|
b.Property<string>("Value")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@ -52,40 +64,24 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("IdCreatedCommit");
|
|
||||||
|
|
||||||
b.HasIndex("IdObsoletedCommit");
|
|
||||||
|
|
||||||
b.ToTable("change_log");
|
b.ToTable("change_log");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.DataScheme", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("DiscriminatorId")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Id коммита");
|
.HasComment("Идентификатор схемы данных");
|
||||||
|
|
||||||
b.Property<string>("Comment")
|
b.Property<string>("PropNames")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text")
|
.HasColumnType("jsonb")
|
||||||
.HasComment("Комментарий к коммиту");
|
.HasComment("Наименования полей в порядке индексации");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("Creation")
|
b.HasKey("DiscriminatorId");
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasComment("Дата создания коммита");
|
|
||||||
|
|
||||||
b.Property<Guid>("DiscriminatorId")
|
b.ToTable("data_scheme");
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasComment("Дискриминатор таблицы");
|
|
||||||
|
|
||||||
b.Property<Guid>("IdAuthor")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasComment("Пользователь, создавший коммит");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("change_log_commit");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
|
||||||
@ -133,30 +129,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
b.ToTable("parameter_data");
|
b.ToTable("parameter_data");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.SchemeProperty", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("DiscriminatorId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasComment("Идентификатор схемы данных");
|
|
||||||
|
|
||||||
b.Property<int>("Index")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasComment("Индекс поля");
|
|
||||||
|
|
||||||
b.Property<byte>("PropertyKind")
|
|
||||||
.HasColumnType("smallint")
|
|
||||||
.HasComment("Тип индексируемого поля");
|
|
||||||
|
|
||||||
b.Property<string>("PropertyName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasComment("Наименования индексируемого поля");
|
|
||||||
|
|
||||||
b.HasKey("DiscriminatorId", "Index");
|
|
||||||
|
|
||||||
b.ToTable("scheme_property");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Key")
|
b.Property<Guid>("Key")
|
||||||
@ -235,23 +207,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
b.ToTable("timestamped_values");
|
b.ToTable("timestamped_values");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "CreatedCommit")
|
|
||||||
.WithMany("ChangeLogCreatedItems")
|
|
||||||
.HasForeignKey("IdCreatedCommit")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "ObsoletedCommit")
|
|
||||||
.WithMany("ChangeLogObsoletedItems")
|
|
||||||
.HasForeignKey("IdObsoletedCommit");
|
|
||||||
|
|
||||||
b.Navigation("CreatedCommit");
|
|
||||||
|
|
||||||
b.Navigation("ObsoletedCommit");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
|
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
|
||||||
@ -263,11 +218,15 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
b.Navigation("System");
|
b.Navigation("System");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("ChangeLogCreatedItems");
|
b.HasOne("DD.Persistence.Database.Entity.DataScheme", "DataScheme")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("DiscriminatorId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("ChangeLogObsoletedItems");
|
b.Navigation("DataScheme");
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
@ -7,24 +7,39 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||||||
namespace DD.Persistence.Database.Postgres.Migrations
|
namespace DD.Persistence.Database.Postgres.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class Innit : Migration
|
public partial class Init : Migration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "change_log_commit",
|
name: "change_log",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id коммита"),
|
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
|
||||||
IdAuthor = table.Column<Guid>(type: "uuid", nullable: false, comment: "Пользователь, создавший коммит"),
|
IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"),
|
||||||
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"),
|
IdAuthor = table.Column<Guid>(type: "uuid", nullable: false, comment: "Автор изменения"),
|
||||||
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания коммита"),
|
IdEditor = table.Column<Guid>(type: "uuid", nullable: true, comment: "Редактор"),
|
||||||
Comment = table.Column<string>(type: "text", nullable: false, comment: "Комментарий к коммиту")
|
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"),
|
||||||
|
Obsolete = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true, comment: "Дата устаревания (например при удалении)"),
|
||||||
|
IdNext = table.Column<Guid>(type: "uuid", nullable: true, comment: "Id заменяющей записи"),
|
||||||
|
Value = table.Column<string>(type: "jsonb", nullable: false, comment: "Значение")
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_change_log_commit", x => x.Id);
|
table.PrimaryKey("PK_change_log", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "data_scheme",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Идентификатор схемы данных"),
|
||||||
|
PropNames = table.Column<string>(type: "jsonb", nullable: false, comment: "Наименования полей в порядке индексации")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_data_scheme", x => x.DiscriminatorId);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
@ -54,20 +69,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
table.PrimaryKey("PK_parameter_data", x => new { x.DiscriminatorId, x.ParameterId, x.Timestamp });
|
table.PrimaryKey("PK_parameter_data", x => new { x.DiscriminatorId, x.ParameterId, x.Timestamp });
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "scheme_property",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Идентификатор схемы данных"),
|
|
||||||
Index = table.Column<int>(type: "integer", nullable: false, comment: "Индекс поля"),
|
|
||||||
PropertyName = table.Column<string>(type: "text", nullable: false, comment: "Наименования индексируемого поля"),
|
|
||||||
PropertyKind = table.Column<byte>(type: "smallint", nullable: false, comment: "Тип индексируемого поля")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_scheme_property", x => new { x.DiscriminatorId, x.Index });
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "setpoint",
|
name: "setpoint",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@ -93,32 +94,12 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_timestamped_values", x => new { x.DiscriminatorId, x.Timestamp });
|
table.PrimaryKey("PK_timestamped_values", x => new { x.DiscriminatorId, x.Timestamp });
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "change_log",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
|
|
||||||
IdNext = table.Column<Guid>(type: "uuid", nullable: true, comment: "Id заменяющей записи"),
|
|
||||||
Value = table.Column<string>(type: "jsonb", nullable: false, comment: "Значение"),
|
|
||||||
IdCreatedCommit = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id коммита на создание записи"),
|
|
||||||
IdObsoletedCommit = table.Column<Guid>(type: "uuid", nullable: true, comment: "Id коммита на устаревание записи")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_change_log", x => x.Id);
|
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_change_log_change_log_commit_IdCreatedCommit",
|
name: "FK_timestamped_values_data_scheme_DiscriminatorId",
|
||||||
column: x => x.IdCreatedCommit,
|
column: x => x.DiscriminatorId,
|
||||||
principalTable: "change_log_commit",
|
principalTable: "data_scheme",
|
||||||
principalColumn: "Id",
|
principalColumn: "DiscriminatorId",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_change_log_change_log_commit_IdObsoletedCommit",
|
|
||||||
column: x => x.IdObsoletedCommit,
|
|
||||||
principalTable: "change_log_commit",
|
|
||||||
principalColumn: "Id");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
@ -143,16 +124,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_change_log_IdCreatedCommit",
|
|
||||||
table: "change_log",
|
|
||||||
column: "IdCreatedCommit");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_change_log_IdObsoletedCommit",
|
|
||||||
table: "change_log",
|
|
||||||
column: "IdObsoletedCommit");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_tech_message_SystemId",
|
name: "IX_tech_message_SystemId",
|
||||||
table: "tech_message",
|
table: "tech_message",
|
||||||
@ -168,9 +139,6 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "parameter_data");
|
name: "parameter_data");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "scheme_property");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "setpoint");
|
name: "setpoint");
|
||||||
|
|
||||||
@ -181,10 +149,10 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
name: "timestamped_values");
|
name: "timestamped_values");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "change_log_commit");
|
name: "data_source_system");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "data_source_system");
|
name: "data_scheme");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -30,17 +30,29 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Ключ записи");
|
.HasComment("Ключ записи");
|
||||||
|
|
||||||
b.Property<Guid>("IdCreatedCommit")
|
b.Property<DateTimeOffset>("Creation")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("Дата создания записи");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdAuthor")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Id коммита на создание записи");
|
.HasComment("Автор изменения");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdDiscriminator")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Дискриминатор таблицы");
|
||||||
|
|
||||||
|
b.Property<Guid?>("IdEditor")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Редактор");
|
||||||
|
|
||||||
b.Property<Guid?>("IdNext")
|
b.Property<Guid?>("IdNext")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Id заменяющей записи");
|
.HasComment("Id заменяющей записи");
|
||||||
|
|
||||||
b.Property<Guid?>("IdObsoletedCommit")
|
b.Property<DateTimeOffset?>("Obsolete")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasComment("Id коммита на устаревание записи");
|
.HasComment("Дата устаревания (например при удалении)");
|
||||||
|
|
||||||
b.Property<string>("Value")
|
b.Property<string>("Value")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@ -49,40 +61,24 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("IdCreatedCommit");
|
b.ToTable("change_log");
|
||||||
|
|
||||||
b.HasIndex("IdObsoletedCommit");
|
|
||||||
|
|
||||||
b.ToTable("change_log", (string)null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.DataScheme", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("DiscriminatorId")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Id коммита");
|
.HasComment("Идентификатор схемы данных");
|
||||||
|
|
||||||
b.Property<string>("Comment")
|
b.Property<string>("PropNames")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text")
|
.HasColumnType("jsonb")
|
||||||
.HasComment("Комментарий к коммиту");
|
.HasComment("Наименования полей в порядке индексации");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("Creation")
|
b.HasKey("DiscriminatorId");
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasComment("Дата создания коммита");
|
|
||||||
|
|
||||||
b.Property<Guid>("DiscriminatorId")
|
b.ToTable("data_scheme");
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasComment("Дискриминатор таблицы");
|
|
||||||
|
|
||||||
b.Property<Guid>("IdAuthor")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasComment("Пользователь, создавший коммит");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("change_log_commit", (string)null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
|
||||||
@ -103,7 +99,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasKey("SystemId");
|
b.HasKey("SystemId");
|
||||||
|
|
||||||
b.ToTable("data_source_system", (string)null);
|
b.ToTable("data_source_system");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b =>
|
||||||
@ -127,31 +123,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
|
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
|
||||||
|
|
||||||
b.ToTable("parameter_data", (string)null);
|
b.ToTable("parameter_data");
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.SchemeProperty", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("DiscriminatorId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasComment("Идентификатор схемы данных");
|
|
||||||
|
|
||||||
b.Property<int>("Index")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasComment("Индекс поля");
|
|
||||||
|
|
||||||
b.Property<byte>("PropertyKind")
|
|
||||||
.HasColumnType("smallint")
|
|
||||||
.HasComment("Тип индексируемого поля");
|
|
||||||
|
|
||||||
b.Property<string>("PropertyName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasComment("Наименования индексируемого поля");
|
|
||||||
|
|
||||||
b.HasKey("DiscriminatorId", "Index");
|
|
||||||
|
|
||||||
b.ToTable("scheme_property", (string)null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
|
||||||
@ -174,7 +146,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasKey("Key", "Timestamp");
|
b.HasKey("Key", "Timestamp");
|
||||||
|
|
||||||
b.ToTable("setpoint", (string)null);
|
b.ToTable("setpoint");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
||||||
@ -209,7 +181,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasIndex("SystemId");
|
b.HasIndex("SystemId");
|
||||||
|
|
||||||
b.ToTable("tech_message", (string)null);
|
b.ToTable("tech_message");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
|
||||||
@ -229,24 +201,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasKey("DiscriminatorId", "Timestamp");
|
b.HasKey("DiscriminatorId", "Timestamp");
|
||||||
|
|
||||||
b.ToTable("timestamped_values", (string)null);
|
b.ToTable("timestamped_values");
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "CreatedCommit")
|
|
||||||
.WithMany("ChangeLogCreatedItems")
|
|
||||||
.HasForeignKey("IdCreatedCommit")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "ObsoletedCommit")
|
|
||||||
.WithMany("ChangeLogObsoletedItems")
|
|
||||||
.HasForeignKey("IdObsoletedCommit");
|
|
||||||
|
|
||||||
b.Navigation("CreatedCommit");
|
|
||||||
|
|
||||||
b.Navigation("ObsoletedCommit");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
||||||
@ -260,11 +215,15 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
b.Navigation("System");
|
b.Navigation("System");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("ChangeLogCreatedItems");
|
b.HasOne("DD.Persistence.Database.Entity.DataScheme", "DataScheme")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("DiscriminatorId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("ChangeLogObsoletedItems");
|
b.Navigation("DataScheme");
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
@ -7,14 +7,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Ardalis.Specification.EntityFrameworkCore" Version="8.0.0" />
|
|
||||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
|
||||||
<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="UuidExtensions" Version="1.2.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
|
using DD.Persistence.Database.EntityAbstractions;
|
||||||
|
using DD.Persistence.ModelsAbstractions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
@ -9,32 +11,29 @@ namespace DD.Persistence.Database.Entity;
|
|||||||
/// Часть записи, описывающая изменение
|
/// Часть записи, описывающая изменение
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Table("change_log")]
|
[Table("change_log")]
|
||||||
public class ChangeLog
|
public class ChangeLog : IChangeLog
|
||||||
{
|
{
|
||||||
[Key, Comment("Ключ записи")]
|
[Key, Comment("Ключ записи")]
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Comment("Дискриминатор таблицы")]
|
||||||
|
public Guid IdDiscriminator { get; set; }
|
||||||
|
|
||||||
|
[Comment("Автор изменения")]
|
||||||
|
public Guid IdAuthor { get; set; }
|
||||||
|
|
||||||
|
[Comment("Редактор")]
|
||||||
|
public Guid? IdEditor { get; set; }
|
||||||
|
|
||||||
|
[Comment("Дата создания записи")]
|
||||||
|
public DateTimeOffset Creation { get; set; }
|
||||||
|
|
||||||
|
[Comment("Дата устаревания (например при удалении)")]
|
||||||
|
public DateTimeOffset? Obsolete { get; set; }
|
||||||
|
|
||||||
[Comment("Id заменяющей записи")]
|
[Comment("Id заменяющей записи")]
|
||||||
public Guid? IdNext { get; set; }
|
public Guid? IdNext { get; set; }
|
||||||
|
|
||||||
[Column(TypeName = "jsonb"), Comment("Значение")]
|
[Column(TypeName = "jsonb"), Comment("Значение")]
|
||||||
public required IDictionary<string, object> Value { get; set; }
|
public required IDictionary<string, object> Value { get; set; }
|
||||||
|
|
||||||
[Required, Comment("Id коммита на создание записи")]
|
|
||||||
public Guid IdCreatedCommit { get; set; }
|
|
||||||
|
|
||||||
[Comment("Id коммита на устаревание записи")]
|
|
||||||
public Guid? IdObsoletedCommit { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// коммит для актуальной записи
|
|
||||||
/// </summary>
|
|
||||||
[Required, ForeignKey(nameof(IdCreatedCommit)), Comment("Коммит пользователя на создание записи")]
|
|
||||||
public virtual ChangeLogCommit CreatedCommit { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// коммит для устаревшей записи
|
|
||||||
/// </summary>
|
|
||||||
[ForeignKey(nameof(IdObsoletedCommit)), Comment("Коммит пользователя на устаревание записи")]
|
|
||||||
public virtual ChangeLogCommit? ObsoletedCommit { get; set; }
|
|
||||||
}
|
}
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Entity;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Таблица c коммитами пользователей
|
|
||||||
/// </summary>
|
|
||||||
[Table("change_log_commit")]
|
|
||||||
public class ChangeLogCommit: IDiscriminatorItem
|
|
||||||
{
|
|
||||||
[Key, Comment("Id коммита")]
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
|
|
||||||
[Comment("Пользователь, создавший коммит")]
|
|
||||||
public Guid IdAuthor { get; set; }
|
|
||||||
|
|
||||||
[Comment("Дискриминатор таблицы")]
|
|
||||||
public Guid DiscriminatorId { get; set; }
|
|
||||||
|
|
||||||
[Comment("Дата создания коммита")]
|
|
||||||
public DateTimeOffset Creation { get; set; }
|
|
||||||
|
|
||||||
[Comment("Комментарий к коммиту")]
|
|
||||||
public required string Comment { get; set; }
|
|
||||||
|
|
||||||
[Required, InverseProperty(nameof(ChangeLog.CreatedCommit)), Comment("Записи, добавленные в журнал изменений")]
|
|
||||||
public virtual ICollection<ChangeLog> ChangeLogCreatedItems { get; set; } = null!;
|
|
||||||
|
|
||||||
[Required, InverseProperty(nameof(ChangeLog.ObsoletedCommit)), Comment("Устаревшие записи в журнале изменений")]
|
|
||||||
public virtual ICollection<ChangeLog> ChangeLogObsoletedItems { get; set; } = null!;
|
|
||||||
}
|
|
15
DD.Persistence.Database/Entity/DataScheme.cs
Normal file
15
DD.Persistence.Database/Entity/DataScheme.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace DD.Persistence.Database.Entity;
|
||||||
|
|
||||||
|
[Table("data_scheme")]
|
||||||
|
public class DataScheme
|
||||||
|
{
|
||||||
|
[Key, Comment("Идентификатор схемы данных"),]
|
||||||
|
public Guid DiscriminatorId { get; set; }
|
||||||
|
|
||||||
|
[Comment("Наименования полей в порядке индексации"), Column(TypeName = "jsonb")]
|
||||||
|
public string[] PropNames { get; set; } = [];
|
||||||
|
}
|
@ -7,7 +7,7 @@ namespace DD.Persistence.Database.Entity;
|
|||||||
|
|
||||||
[Table("parameter_data")]
|
[Table("parameter_data")]
|
||||||
[PrimaryKey(nameof(DiscriminatorId), nameof(ParameterId), nameof(Timestamp))]
|
[PrimaryKey(nameof(DiscriminatorId), nameof(ParameterId), nameof(Timestamp))]
|
||||||
public class ParameterData : IDiscriminatorItem, ITimestampedItem
|
public class ParameterData : ITimestampedItem
|
||||||
{
|
{
|
||||||
[Required, Comment("Дискриминатор системы")]
|
[Required, Comment("Дискриминатор системы")]
|
||||||
public Guid DiscriminatorId { get; set; }
|
public Guid DiscriminatorId { get; set; }
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Entity;
|
|
||||||
|
|
||||||
[Table("scheme_property")]
|
|
||||||
[PrimaryKey(nameof(DiscriminatorId), nameof(Index))]
|
|
||||||
public class SchemeProperty : IDiscriminatorItem
|
|
||||||
{
|
|
||||||
[Comment("Идентификатор схемы данных")]
|
|
||||||
public Guid DiscriminatorId { get; set; }
|
|
||||||
|
|
||||||
[Comment("Индекс поля")]
|
|
||||||
public int Index { get; set; }
|
|
||||||
|
|
||||||
[Comment("Наименования индексируемого поля")]
|
|
||||||
public required string PropertyName { get; set; }
|
|
||||||
|
|
||||||
[Comment("Тип индексируемого поля")]
|
|
||||||
public required JsonValueKind PropertyKind { get; set; }
|
|
||||||
}
|
|
@ -7,7 +7,7 @@ namespace DD.Persistence.Database.Entity;
|
|||||||
|
|
||||||
[Table("timestamped_values")]
|
[Table("timestamped_values")]
|
||||||
[PrimaryKey(nameof(DiscriminatorId), nameof(Timestamp))]
|
[PrimaryKey(nameof(DiscriminatorId), nameof(Timestamp))]
|
||||||
public class TimestampedValues : IDiscriminatorItem, ITimestampedItem, IValuesItem
|
public class TimestampedValues : ITimestampedItem
|
||||||
{
|
{
|
||||||
[Comment("Временная отметка"), Key]
|
[Comment("Временная отметка"), Key]
|
||||||
public DateTimeOffset Timestamp { get; set; }
|
public DateTimeOffset Timestamp { get; set; }
|
||||||
@ -17,4 +17,7 @@ public class TimestampedValues : IDiscriminatorItem, ITimestampedItem, IValuesIt
|
|||||||
|
|
||||||
[Comment("Данные"), Column(TypeName = "jsonb")]
|
[Comment("Данные"), Column(TypeName = "jsonb")]
|
||||||
public required object[] Values { get; set; }
|
public required object[] Values { get; set; }
|
||||||
|
|
||||||
|
[Required, ForeignKey(nameof(DiscriminatorId)), Comment("Идентификаторы")]
|
||||||
|
public virtual DataScheme? DataScheme { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,16 @@ public interface IChangeLog
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Автор изменения
|
||||||
|
/// </summary>
|
||||||
|
public Guid IdAuthor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Редактор
|
||||||
|
/// </summary>
|
||||||
|
public Guid? IdEditor { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата создания записи
|
/// Дата создания записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -20,4 +30,18 @@ public interface IChangeLog
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTimeOffset? Obsolete { get; set; }
|
public DateTimeOffset? Obsolete { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id заменяющей записи
|
||||||
|
/// </summary>
|
||||||
|
public Guid? IdNext { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дискриминатор таблицы
|
||||||
|
/// </summary>
|
||||||
|
public Guid IdDiscriminator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Значение
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, object> Value { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
namespace DD.Persistence.Database.EntityAbstractions;
|
|
||||||
public interface IDiscriminatorItem
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Дискриминатор
|
|
||||||
/// </summary>
|
|
||||||
Guid DiscriminatorId { get; set; }
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using DD.Persistence.Database.Entity;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.EntityAbstractions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Сущность с данными, принадлежащими к определенной схеме
|
|
||||||
/// </summary>
|
|
||||||
public interface IValuesItem
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Значения
|
|
||||||
/// </summary>
|
|
||||||
object[] Values { get; set; }
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.Helpers;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Extensions;
|
|
||||||
|
|
||||||
public static class SpecificationExtensions
|
|
||||||
{
|
|
||||||
public static Expression<Func<T, bool>>? Or<T>(this ISpecification<T> spec, ISpecification<T> otherSpec)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "x");
|
|
||||||
|
|
||||||
var exprSpec1 = CombineWhereExpressions(spec.WhereExpressions, parameter);
|
|
||||||
var exprSpec2 = CombineWhereExpressions(otherSpec.WhereExpressions, parameter);
|
|
||||||
|
|
||||||
Expression? orExpression = exprSpec1 is not null && exprSpec2 is not null
|
|
||||||
? Expression.OrElse(exprSpec1, exprSpec2)
|
|
||||||
: exprSpec1 ?? exprSpec2;
|
|
||||||
|
|
||||||
if (orExpression is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var lambdaExpr = Expression.Lambda<Func<T, bool>>(orExpression, parameter);
|
|
||||||
|
|
||||||
return lambdaExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Expression? CombineWhereExpressions<T>(IEnumerable<WhereExpressionInfo<T>> whereExpressions, ParameterExpression parameter)
|
|
||||||
{
|
|
||||||
Expression? newExpr = null;
|
|
||||||
foreach (var where in whereExpressions)
|
|
||||||
{
|
|
||||||
var expr = ParameterReplacerVisitor.Replace(where.Filter.Body, where.Filter.Parameters[0], parameter);
|
|
||||||
newExpr = newExpr is null ? expr : Expression.AndAlso(newExpr, expr);
|
|
||||||
}
|
|
||||||
return newExpr;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using Ardalis.Specification.EntityFrameworkCore;
|
|
||||||
using DD.Persistence.Database.Entity;
|
|
||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
using DD.Persistence.Database.Specifications.Operation;
|
|
||||||
using DD.Persistence.Database.Specifications.ValuesItem;
|
|
||||||
using DD.Persistence.Filter.Models;
|
|
||||||
using DD.Persistence.Filter.Models.Abstractions;
|
|
||||||
using DD.Persistence.Filter.Models.Enumerations;
|
|
||||||
using DD.Persistence.Filter.Visitors;
|
|
||||||
using DD.Persistence.Models;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Helpers;
|
|
||||||
public static class FilterBuilder
|
|
||||||
{
|
|
||||||
public static IQueryable<TEntity> ApplyFilter<TEntity>(this IQueryable<TEntity> query, DataSchemeDto dataSchemeDto, TNode root)
|
|
||||||
where TEntity : class, IValuesItem
|
|
||||||
{
|
|
||||||
var filterSpec = dataSchemeDto.BuildFilter<TEntity>(root);
|
|
||||||
if (filterSpec != null)
|
|
||||||
return query.WithSpecification(filterSpec);
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ISpecification<TEntity>? BuildFilter<TEntity>(this DataSchemeDto dataSchemeDto, TNode root)
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
var result = dataSchemeDto.BuildSpecificationByNextNode<TEntity>(root);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ISpecification<TEntity>? BuildSpecificationByNextNode<TEntity>(this DataSchemeDto dataSchemeDto, TNode node)
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
var visitor = new NodeVisitor<ISpecification<TEntity>?>(
|
|
||||||
dataSchemeDto.VertexProcessing<TEntity>,
|
|
||||||
dataSchemeDto.LeafProcessing<TEntity>
|
|
||||||
);
|
|
||||||
|
|
||||||
var result = node.AcceptVisitor(visitor);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ISpecification<TEntity>? VertexProcessing<TEntity>(this DataSchemeDto dataSchemeDto, TVertex vertex)
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
var leftSpecification = dataSchemeDto.BuildSpecificationByNextNode<TEntity>(vertex.Left);
|
|
||||||
var rigthSpecification = dataSchemeDto.BuildSpecificationByNextNode<TEntity>(vertex.Rigth);
|
|
||||||
if (leftSpecification is null)
|
|
||||||
return rigthSpecification;
|
|
||||||
if (rigthSpecification is null)
|
|
||||||
return leftSpecification;
|
|
||||||
|
|
||||||
ISpecification<TEntity>? result = null;
|
|
||||||
switch (vertex.Operation)
|
|
||||||
{
|
|
||||||
case OperationEnum.And:
|
|
||||||
result = new AndSpec<TEntity>(leftSpecification, rigthSpecification);
|
|
||||||
break;
|
|
||||||
case OperationEnum.Or:
|
|
||||||
result = new OrSpec<TEntity>(leftSpecification, rigthSpecification);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ISpecification<TEntity>? LeafProcessing<TEntity>(this DataSchemeDto dataSchemeDto, TLeaf leaf)
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
var schemeProperty = dataSchemeDto.FirstOrDefault(e => e.PropertyName.Equals(leaf.PropName));
|
|
||||||
if (schemeProperty is null)
|
|
||||||
throw new ArgumentException($"Свойство {leaf.PropName} не найдено в схеме данных");
|
|
||||||
|
|
||||||
ISpecification<TEntity>? result = null;
|
|
||||||
switch (schemeProperty.PropertyKind)
|
|
||||||
{
|
|
||||||
case JsonValueKind.String:
|
|
||||||
var stringValue = Convert.ToString(leaf.Value);
|
|
||||||
var stringSpecifications = StringSpecifications<TEntity>();
|
|
||||||
result = stringSpecifications[leaf.Operation](schemeProperty.Index, stringValue);
|
|
||||||
break;
|
|
||||||
case JsonValueKind.Number:
|
|
||||||
var doubleValue = Convert.ToDouble(leaf.Value);
|
|
||||||
var doubleSpecifications = DoubleSpecifications<TEntity>();
|
|
||||||
result = doubleSpecifications[leaf.Operation](schemeProperty.Index, doubleValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<OperationEnum, Func<int, string?, ISpecification<TEntity>>> StringSpecifications<TEntity>()
|
|
||||||
where TEntity : IValuesItem => new()
|
|
||||||
{
|
|
||||||
{ OperationEnum.Equal, (int index, string? value) => new ValueEqualSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.NotEqual, (int index, string? value) => new ValueNotEqualSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.Greate, (int index, string? value) => new ValueGreateSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.GreateOrEqual, (int index, string? value) => new ValueGreateOrEqualSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.Less, (int index, string? value) => new ValueLessSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.LessOrEqual, (int index, string? value) => new ValueLessOrEqualSpec<TEntity>(index, value) }
|
|
||||||
};
|
|
||||||
private static Dictionary<OperationEnum, Func<int, double?, ISpecification<TEntity>>> DoubleSpecifications<TEntity>()
|
|
||||||
where TEntity : IValuesItem => new()
|
|
||||||
{
|
|
||||||
{ OperationEnum.Equal, (int index, double? value) => new ValueEqualSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.NotEqual, (int index, double? value) => new ValueNotEqualSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.Greate, (int index, double? value) => new ValueGreateSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.GreateOrEqual, (int index, double? value) => new ValueGreateOrEqualSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.Less, (int index, double? value) => new ValueLessSpec<TEntity>(index, value) },
|
|
||||||
{ OperationEnum.LessOrEqual, (int index, double? value) => new ValueLessOrEqualSpec<TEntity>(index, value) }
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Helpers;
|
|
||||||
public class ParameterReplacerVisitor : ExpressionVisitor
|
|
||||||
{
|
|
||||||
private readonly Expression _newExpression;
|
|
||||||
private readonly ParameterExpression _oldParameter;
|
|
||||||
|
|
||||||
private ParameterReplacerVisitor(ParameterExpression oldParameter, Expression newExpression)
|
|
||||||
{
|
|
||||||
_oldParameter = oldParameter;
|
|
||||||
_newExpression = newExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Expression Replace(Expression expression, ParameterExpression oldParameter, Expression newExpression)
|
|
||||||
=> new ParameterReplacerVisitor(oldParameter, newExpression).Visit(expression);
|
|
||||||
|
|
||||||
protected override Expression VisitParameter(ParameterExpression p)
|
|
||||||
=> p == _oldParameter ? _newExpression : p;
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
using DD.Persistence.Database.Entity;
|
|
||||||
using DD.Persistence.Models;
|
|
||||||
using DD.Persistence.Models.ChangeLog;
|
|
||||||
using Mapster;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database;
|
|
||||||
public static class MapsterSetup
|
|
||||||
{
|
|
||||||
public static void Configure()
|
|
||||||
{
|
|
||||||
TypeAdapterConfig.GlobalSettings.Default.Config
|
|
||||||
.ForType<TechMessageDto, TechMessage>()
|
|
||||||
.Ignore(dest => dest.System, dest => dest.SystemId);
|
|
||||||
|
|
||||||
TypeAdapterConfig<ChangeLog, ChangeLogDto>.NewConfig()
|
|
||||||
.Map(dest => dest.Value, src => new ChangeLogBaseDto()
|
|
||||||
{
|
|
||||||
Value = src.Value,
|
|
||||||
Id = src.Id
|
|
||||||
});
|
|
||||||
|
|
||||||
TypeAdapterConfig<KeyValuePair<Guid, SchemePropertyDto>, SchemeProperty>.NewConfig()
|
|
||||||
.Map(dest => dest.DiscriminatorId, src => src.Key)
|
|
||||||
.Map(dest => dest.Index, src => src.Value.Index)
|
|
||||||
.Map(dest => dest.PropertyKind, src => src.Value.PropertyKind)
|
|
||||||
.Map(dest => dest.PropertyName, src => src.Value.PropertyName);
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,14 +10,12 @@ public class PersistenceDbContext : DbContext
|
|||||||
{
|
{
|
||||||
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
||||||
|
|
||||||
public DbSet<SchemeProperty> SchemeProperty => Set<SchemeProperty>();
|
public DbSet<DataScheme> DataSchemes => Set<DataScheme>();
|
||||||
|
|
||||||
public DbSet<TimestampedValues> TimestampedValues => Set<TimestampedValues>();
|
public DbSet<TimestampedValues> TimestampedValues => Set<TimestampedValues>();
|
||||||
|
|
||||||
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
|
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
|
||||||
|
|
||||||
public DbSet<ChangeLogCommit> ChangeLogCommit => Set<ChangeLogCommit>();
|
|
||||||
|
|
||||||
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
|
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
|
||||||
|
|
||||||
public DbSet<ParameterData> ParameterData => Set<ParameterData>();
|
public DbSet<ParameterData> ParameterData => Set<ParameterData>();
|
||||||
@ -32,6 +30,10 @@ public class PersistenceDbContext : DbContext
|
|||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
modelBuilder.Entity<DataScheme>()
|
||||||
|
.Property(e => e.PropNames)
|
||||||
|
.HasJsonConversion();
|
||||||
|
|
||||||
modelBuilder.Entity<TimestampedValues>()
|
modelBuilder.Entity<TimestampedValues>()
|
||||||
.Property(e => e.Values)
|
.Property(e => e.Values)
|
||||||
.HasJsonConversion();
|
.HasJsonConversion();
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.Entity;
|
|
||||||
using DD.Persistence.Models.ChangeLog;
|
|
||||||
using DD.Persistence.Models.Common;
|
|
||||||
using DD.Persistence.Repositories;
|
|
||||||
using Mapster;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using UuidExtensions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Repositories;
|
|
||||||
public class ChangeLogCommitRepository : IChangeLogCommitRepository
|
|
||||||
{
|
|
||||||
private DbContext db;
|
|
||||||
|
|
||||||
public ChangeLogCommitRepository(DbContext db)
|
|
||||||
{
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ChangeLogCommitDto> Add(ChangeLogCommitCreateRequest request, CancellationToken token)
|
|
||||||
{
|
|
||||||
var commit = new ChangeLogCommit()
|
|
||||||
{
|
|
||||||
Id = Uuid7.Guid(),
|
|
||||||
Creation = DateTimeOffset.UtcNow,
|
|
||||||
DiscriminatorId = request.DiscriminatorId,
|
|
||||||
IdAuthor = request.IdAuthor,
|
|
||||||
Comment = request.Comment,
|
|
||||||
};
|
|
||||||
|
|
||||||
db.Set<ChangeLogCommit>().Add(commit);
|
|
||||||
await db.SaveChangesAsync(token);
|
|
||||||
|
|
||||||
var commitDto = commit.Adapt<ChangeLogCommitDto>();
|
|
||||||
return commitDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ChangeLogCommitDto>> GetHistory(ChangeLogQuery request, CancellationToken token)
|
|
||||||
{
|
|
||||||
var query = db.Set<ChangeLogCommit>()
|
|
||||||
.Where(e => e.DiscriminatorId == request.DiscriminatorId);
|
|
||||||
|
|
||||||
if (request.UserId.HasValue)
|
|
||||||
query = query.Where(e => e.IdAuthor == request.UserId);
|
|
||||||
|
|
||||||
if(request.GeDate.HasValue)
|
|
||||||
query = query.Where(e => e.Creation >= request.GeDate);
|
|
||||||
|
|
||||||
if (request.LeDate.HasValue)
|
|
||||||
query = query.Where(e => e.Creation <= request.LeDate);
|
|
||||||
|
|
||||||
query = query
|
|
||||||
.Include(e => e.ChangeLogCreatedItems)
|
|
||||||
.Include(e => e.ChangeLogObsoletedItems);
|
|
||||||
|
|
||||||
var entities = await query.ToArrayAsync(token);
|
|
||||||
|
|
||||||
var dtos = entities.Select(entity => new ChangeLogCommitDto()
|
|
||||||
{
|
|
||||||
ChangeLogCreatedItems = entity.ChangeLogCreatedItems.Select(c => c.Adapt<ChangeLogDto>()),
|
|
||||||
ChangeLogObsoletedItems = entity.ChangeLogObsoletedItems.Select(c => c.Adapt<ChangeLogDto>()),
|
|
||||||
Comment = entity.Comment,
|
|
||||||
Creation = entity.Creation,
|
|
||||||
DiscriminatorId = entity.DiscriminatorId,
|
|
||||||
IdAuthor = entity.IdAuthor,
|
|
||||||
Id = entity.Id,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
return dtos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ChangeLogStatisticsDto>> GetStatistics(Guid discriminatorId, Guid? userId, CancellationToken token)
|
|
||||||
{
|
|
||||||
var query = db.Set<ChangeLogCommit>()
|
|
||||||
.Where(e => e.DiscriminatorId == discriminatorId);
|
|
||||||
|
|
||||||
if (userId.HasValue)
|
|
||||||
query = query.Where(e => e.IdAuthor == userId);
|
|
||||||
|
|
||||||
var queryStat = query
|
|
||||||
.GroupBy(e => e.Creation.Date)
|
|
||||||
.Select(group => new
|
|
||||||
{
|
|
||||||
Date = group.Key,
|
|
||||||
CommitCount = group.Count(),
|
|
||||||
CreatedChangeLogCount = group.Sum(e => e.ChangeLogCreatedItems.Count()),
|
|
||||||
ObsoletedChangeLogCount = group.Sum(e => e.ChangeLogObsoletedItems.Count()),
|
|
||||||
});
|
|
||||||
var entities = await queryStat.ToListAsync(token);
|
|
||||||
var dtos = entities.Select(e => new ChangeLogStatisticsDto
|
|
||||||
{
|
|
||||||
CommitCount = e.CommitCount,
|
|
||||||
Date = new DateOnly(e.Date.Year, e.Date.Month, e.Date.Day),
|
|
||||||
CreatedChangeLogCount = e.CreatedChangeLogCount,
|
|
||||||
ObsoletedCount = e.ObsoletedChangeLogCount,
|
|
||||||
});
|
|
||||||
return dtos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
|
|
||||||
{
|
|
||||||
var query = db.Set<ChangeLogCommit>()
|
|
||||||
.Where(e => e.DiscriminatorId == discriminatorId)
|
|
||||||
.GroupBy(e => true)
|
|
||||||
.Select(group => new
|
|
||||||
{
|
|
||||||
Min = group.Min(e => e.Creation),
|
|
||||||
Max = group.Max(e => e.Creation)
|
|
||||||
});
|
|
||||||
|
|
||||||
var values = await query.FirstOrDefaultAsync(token);
|
|
||||||
|
|
||||||
if (values is null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DatesRangeDto
|
|
||||||
{
|
|
||||||
From = values.Min,
|
|
||||||
To = values.Max,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<DateOnly>> GetDatesChange(Guid discriminatorId, CancellationToken token)
|
|
||||||
{
|
|
||||||
var query = db.Set<ChangeLogCommit>()
|
|
||||||
.Where(e => e.DiscriminatorId == discriminatorId)
|
|
||||||
.GroupBy(e => e.Creation.Date)
|
|
||||||
.Select(g => g.Key);
|
|
||||||
var entities = await query.ToArrayAsync(token);
|
|
||||||
var dates = entities
|
|
||||||
.Select(e => new DateOnly(e.Year, e.Month, e.Minute));
|
|
||||||
return dates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
|
|
||||||
{
|
|
||||||
var min = dateBegin.ToUniversalTime();
|
|
||||||
var max = dateEnd.ToUniversalTime();
|
|
||||||
|
|
||||||
var query = db.Set<ChangeLogCommit>()
|
|
||||||
.Where(c => c.DiscriminatorId == idDiscriminator)
|
|
||||||
.Where(c => c.Creation >= min && c.Creation <= max)
|
|
||||||
.Include(c => c.ChangeLogCreatedItems)
|
|
||||||
.Include(c => c.ChangeLogObsoletedItems);
|
|
||||||
|
|
||||||
var entities = await query
|
|
||||||
.AsNoTracking()
|
|
||||||
.ToArrayAsync(token);
|
|
||||||
|
|
||||||
var dtos = entities
|
|
||||||
.SelectMany(c => new[] { c.ChangeLogCreatedItems, c.ChangeLogObsoletedItems })
|
|
||||||
.SelectMany(c => c)
|
|
||||||
.Where(c => c != null)
|
|
||||||
.Select(ConvertChangeLogToDto);
|
|
||||||
|
|
||||||
return dtos;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChangeLogDto ConvertChangeLogToDto(ChangeLog entity)
|
|
||||||
{
|
|
||||||
var dto = entity.Adapt<ChangeLogDto>();
|
|
||||||
dto.Creation = entity.CreatedCommit?.Creation;
|
|
||||||
dto.Obsolete = entity.ObsoletedCommit?.Creation;
|
|
||||||
dto.IdAuthor = entity.CreatedCommit?.IdAuthor;
|
|
||||||
dto.IdEditor = entity.ObsoletedCommit?.IdAuthor;
|
|
||||||
return dto;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,193 +0,0 @@
|
|||||||
using DD.Persistence.Database.Entity;
|
|
||||||
using DD.Persistence.Database.Postgres.Helpers;
|
|
||||||
using DD.Persistence.Models.ChangeLog;
|
|
||||||
using DD.Persistence.Models.Common;
|
|
||||||
using DD.Persistence.Models.Requests;
|
|
||||||
using DD.Persistence.Repositories;
|
|
||||||
using Mapster;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System;
|
|
||||||
using UuidExtensions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Repositories;
|
|
||||||
public class ChangeLogRepository : IChangeLogRepository
|
|
||||||
{
|
|
||||||
private readonly DbContext db;
|
|
||||||
|
|
||||||
public ChangeLogRepository(DbContext db)
|
|
||||||
{
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> AddRange(ChangeLogCommitDto commitDto, IEnumerable<IDictionary<string, object>> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
var entities = new List<ChangeLog>();
|
|
||||||
foreach (var dto in dtos)
|
|
||||||
{
|
|
||||||
var entity = new ChangeLog()
|
|
||||||
{
|
|
||||||
Id = Uuid7.Guid(),
|
|
||||||
IdCreatedCommit = commitDto.Id,
|
|
||||||
Value = dto,
|
|
||||||
};
|
|
||||||
|
|
||||||
entities.Add(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Set<ChangeLog>().AddRange(entities);
|
|
||||||
|
|
||||||
var result = await db.SaveChangesAsync(token);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> MarkAsDeleted(IEnumerable<Guid> ids, ChangeLogCommitDto commit, CancellationToken token)
|
|
||||||
{
|
|
||||||
var query = db.Set<ChangeLog>()
|
|
||||||
.Where(s => ids.Contains(s.Id))
|
|
||||||
.Where(s => s.IdObsoletedCommit == null);
|
|
||||||
|
|
||||||
if (query.Count() != ids.Count())
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Count of active items not equal count of ids", nameof(ids));
|
|
||||||
}
|
|
||||||
|
|
||||||
var entities = await query.ToArrayAsync(token);
|
|
||||||
|
|
||||||
foreach (var entity in entities)
|
|
||||||
{
|
|
||||||
entity.IdObsoletedCommit = commit.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await db.SaveChangesAsync(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> MarkAsDeletedByDiscriminator(ChangeLogCommitDto commit, CancellationToken token)
|
|
||||||
{
|
|
||||||
var query = db.Set<ChangeLog>()
|
|
||||||
.Where(s => s.CreatedCommit.DiscriminatorId == commit.DiscriminatorId)
|
|
||||||
.Where(e => e.IdObsoletedCommit == null);
|
|
||||||
|
|
||||||
var entities = await query.ToArrayAsync(token);
|
|
||||||
|
|
||||||
foreach (var entity in entities)
|
|
||||||
{
|
|
||||||
entity.IdObsoletedCommit = commit.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await db.SaveChangesAsync(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> ClearAndAddRange(ChangeLogCommitDto commit, IEnumerable<IDictionary<string, object>> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
var result = 0;
|
|
||||||
|
|
||||||
using var transaction = await db.Database.BeginTransactionAsync(token);
|
|
||||||
|
|
||||||
result += await MarkAsDeletedByDiscriminator(commit, token);
|
|
||||||
result += await AddRange(commit, dtos, token);
|
|
||||||
|
|
||||||
await transaction.CommitAsync(token);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> UpdateRange(ChangeLogCommitDto commitDto, IEnumerable<ChangeLogBaseDto> dtos, CancellationToken token)
|
|
||||||
{
|
|
||||||
var dbSet = db.Set<ChangeLog>();
|
|
||||||
|
|
||||||
var updatedIds = dtos.Select(d => d.Id);
|
|
||||||
var updatedEntities = dbSet
|
|
||||||
.Where(s => updatedIds.Contains(s.Id))
|
|
||||||
.ToDictionary(s => s.Id);
|
|
||||||
|
|
||||||
using var transaction = await db.Database.BeginTransactionAsync(token);
|
|
||||||
|
|
||||||
foreach (var dto in dtos)
|
|
||||||
{
|
|
||||||
var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id);
|
|
||||||
if (updatedEntity is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto));
|
|
||||||
}
|
|
||||||
|
|
||||||
var newEntity = new ChangeLog()
|
|
||||||
{
|
|
||||||
Id = Uuid7.Guid(),
|
|
||||||
Value = dto.Value,
|
|
||||||
IdCreatedCommit = commitDto.Id,
|
|
||||||
};
|
|
||||||
dbSet.Add(newEntity);
|
|
||||||
|
|
||||||
updatedEntity.IdNext = newEntity.Id;
|
|
||||||
updatedEntity.IdObsoletedCommit = commitDto.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await db.SaveChangesAsync(token);
|
|
||||||
await transaction.CommitAsync(token);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PaginationContainer<ChangeLogBaseDto>> GetByDate(
|
|
||||||
Guid idDiscriminator,
|
|
||||||
DateTimeOffset moment,
|
|
||||||
PaginationRequest paginationRequest,
|
|
||||||
CancellationToken token)
|
|
||||||
{
|
|
||||||
var momentUtc = moment.ToUniversalTime();
|
|
||||||
var query = db.Set<ChangeLog>()
|
|
||||||
.Where(s => s.CreatedCommit.DiscriminatorId == idDiscriminator)
|
|
||||||
.Where(e => e.CreatedCommit.Creation <= momentUtc)
|
|
||||||
.Where(e => e.ObsoletedCommit == null || e.ObsoletedCommit.Creation >= momentUtc)
|
|
||||||
.AsNoTracking();
|
|
||||||
|
|
||||||
var result = await query.ApplyPagination(paginationRequest, Convert, token);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ChangeLogBaseDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
|
|
||||||
{
|
|
||||||
var date = dateBegin.ToUniversalTime();
|
|
||||||
var query = db.Set<ChangeLog>()
|
|
||||||
.Where(e => e.CreatedCommit.DiscriminatorId == idDiscriminator)
|
|
||||||
.Where(e => e.CreatedCommit.Creation >= date || (e.IdObsoletedCommit != null && e.ObsoletedCommit!.Creation >= date));
|
|
||||||
|
|
||||||
var entities = await query
|
|
||||||
.AsNoTracking()
|
|
||||||
.ToArrayAsync(token);
|
|
||||||
|
|
||||||
var dtos = entities.Select(Convert);
|
|
||||||
|
|
||||||
return dtos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<ChangeLogDto>> GetByUserIdAndDiscriminatorId(ChangeLogQuery request, CancellationToken token)
|
|
||||||
{
|
|
||||||
var query = db.Set<ChangeLog>()
|
|
||||||
.Include(c => c.CreatedCommit)
|
|
||||||
.Include(c => c.ObsoletedCommit)
|
|
||||||
.Where(c => c.CreatedCommit.DiscriminatorId == request.DiscriminatorId)
|
|
||||||
.Where(c => c.CreatedCommit.IdAuthor == request.UserId
|
|
||||||
|| (c.ObsoletedCommit != null && c.ObsoletedCommit!.IdAuthor == request.UserId));
|
|
||||||
|
|
||||||
var entities = await query
|
|
||||||
.AsNoTracking()
|
|
||||||
.ToArrayAsync(token);
|
|
||||||
var dtos = entities.Select(ConvertFull);
|
|
||||||
|
|
||||||
return dtos;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChangeLogBaseDto Convert(ChangeLog entity) => entity.Adapt<ChangeLogBaseDto>();
|
|
||||||
|
|
||||||
private ChangeLogDto ConvertFull(ChangeLog entity)
|
|
||||||
{
|
|
||||||
var dto = entity.Adapt<ChangeLogDto>();
|
|
||||||
dto.Creation = entity.CreatedCommit.Creation;
|
|
||||||
dto.Obsolete = entity.ObsoletedCommit?.Creation;
|
|
||||||
dto.IdAuthor = entity.CreatedCommit.IdAuthor;
|
|
||||||
dto.IdEditor = entity.ObsoletedCommit?.IdAuthor;
|
|
||||||
return dto;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
using DD.Persistence.Database.Entity;
|
|
||||||
using DD.Persistence.Models;
|
|
||||||
using DD.Persistence.Repositories;
|
|
||||||
using Mapster;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Repositories;
|
|
||||||
public class SchemePropertyRepository : ISchemePropertyRepository
|
|
||||||
{
|
|
||||||
protected DbContext db;
|
|
||||||
public SchemePropertyRepository(DbContext db)
|
|
||||||
{
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
protected IQueryable<SchemeProperty> GetQueryReadOnly() => db.Set<SchemeProperty>();
|
|
||||||
|
|
||||||
public virtual async Task AddRange(DataSchemeDto dataSchemeDto, CancellationToken token)
|
|
||||||
{
|
|
||||||
var entities = dataSchemeDto.Select(e =>
|
|
||||||
KeyValuePair.Create(dataSchemeDto.DiscriminatorId, e)
|
|
||||||
.Adapt<SchemeProperty>()
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.Set<SchemeProperty>().AddRangeAsync(entities, token);
|
|
||||||
await db.SaveChangesAsync(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual async Task<DataSchemeDto?> Get(Guid dataSchemeId, CancellationToken token)
|
|
||||||
{
|
|
||||||
var query = GetQueryReadOnly()
|
|
||||||
.Where(e => e.DiscriminatorId == dataSchemeId);
|
|
||||||
var entities = await query.ToArrayAsync(token);
|
|
||||||
|
|
||||||
DataSchemeDto? result = null;
|
|
||||||
if (entities.Length != 0)
|
|
||||||
{
|
|
||||||
var properties = entities.Select(e => e.Adapt<SchemePropertyDto>()).ToArray();
|
|
||||||
result = new DataSchemeDto(dataSchemeId, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Specifications.Operation;
|
|
||||||
public class AndSpec<TEntity> : Specification<TEntity>
|
|
||||||
{
|
|
||||||
public AndSpec(ISpecification<TEntity> first, ISpecification<TEntity> second)
|
|
||||||
{
|
|
||||||
if (first is null || second is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ApplyCriteria(first);
|
|
||||||
ApplyCriteria(second);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyCriteria(ISpecification<TEntity> specification)
|
|
||||||
{
|
|
||||||
foreach (var criteria in specification.WhereExpressions)
|
|
||||||
{
|
|
||||||
Query.Where(criteria.Filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.Postgres.Extensions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Specifications.Operation;
|
|
||||||
public class OrSpec<TEntity> : Specification<TEntity>
|
|
||||||
{
|
|
||||||
public OrSpec(ISpecification<TEntity> first, ISpecification<TEntity> second)
|
|
||||||
{
|
|
||||||
var orExpression = first.Or(second);
|
|
||||||
if (orExpression == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Query.Where(orExpression);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Specifications.ValuesItem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Спецификация эквивалентности значений IValuesItem в соответствии с индексацией
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class ValueEqualSpec<TEntity> : Specification<TEntity>
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
public ValueEqualSpec(int index, string? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => Convert.ToString(e.Values[index]) == value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueEqualSpec(int index, double? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => Convert.ToDouble(e.Values[index]) == value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Specifications.ValuesItem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Спецификация "больше либо равно" для значений IValuesItem в соответствии с индексацией
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class ValueGreateOrEqualSpec<TEntity> : Specification<TEntity>
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
public ValueGreateOrEqualSpec(int index, string? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueGreateOrEqualSpec(int index, double? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => Convert.ToDouble(e.Values[index]) >= value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Specifications.ValuesItem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Спецификация "больше" для значений IValuesItem в соответствии с индексацией
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class ValueGreateSpec<TEntity> : Specification<TEntity>
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
public ValueGreateSpec(int index, string? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueGreateSpec(int index, double? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => Convert.ToDouble(e.Values[index]) > value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Specifications.ValuesItem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Спецификация "меньше либо равно" для значений IValuesItem в соответствии с индексацией
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class ValueLessOrEqualSpec<TEntity> : Specification<TEntity>
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
public ValueLessOrEqualSpec(int index, string? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) <= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueLessOrEqualSpec(int index, double? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => Convert.ToDouble(e.Values[index]) <= value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Specifications.ValuesItem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Спецификация "меньше" для значений IValuesItem в соответствии с индексацией
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class ValueLessSpec<TEntity> : Specification<TEntity>
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
public ValueLessSpec(int index, string? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) < 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueLessSpec(int index, double? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => Convert.ToDouble(e.Values[index]) < value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using Ardalis.Specification;
|
|
||||||
using DD.Persistence.Database.EntityAbstractions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Specifications.ValuesItem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Спецификация неравенства значений IValuesItem в соответствии с индексацией
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class ValueNotEqualSpec<TEntity> : Specification<TEntity>
|
|
||||||
where TEntity : IValuesItem
|
|
||||||
{
|
|
||||||
public ValueNotEqualSpec(int index, string? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => Convert.ToString(e.Values[index]) != value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueNotEqualSpec(int index, double? value)
|
|
||||||
{
|
|
||||||
Query.Where(e => Convert.ToDouble(e.Values[index]) != value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ using Microsoft.IdentityModel.Tokens;
|
|||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace DD.Persistence.IntegrationTests;
|
namespace DD.Persistence.IntegrationTests;
|
||||||
|
|
||||||
@ -39,11 +40,12 @@ public static class ApiTokenHelper
|
|||||||
var nameIdetifier = Guid.NewGuid().ToString();
|
var nameIdetifier = Guid.NewGuid().ToString();
|
||||||
var claims = new List<Claim>()
|
var claims = new List<Claim>()
|
||||||
{
|
{
|
||||||
|
new(ClaimTypes.NameIdentifier, nameIdetifier),
|
||||||
new("client_id", authUser.ClientId),
|
new("client_id", authUser.ClientId),
|
||||||
new("username", authUser.Username),
|
new("username", authUser.Username),
|
||||||
new("password", authUser.Password),
|
new("password", authUser.Password),
|
||||||
new("grant_type", authUser.GrantType),
|
new("grant_type", authUser.GrantType),
|
||||||
new(ClaimTypes.NameIdentifier.ToString(), "067beb95-3b6d-7370-8000-1b9b336a6cdc")
|
new(ClaimTypes.NameIdentifier.ToString(), Guid.NewGuid().ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenDescriptor = new SecurityTokenDescriptor
|
var tokenDescriptor = new SecurityTokenDescriptor
|
||||||
|
@ -3,22 +3,19 @@ using DD.Persistence.Client.Clients;
|
|||||||
using DD.Persistence.Client.Clients.Interfaces;
|
using DD.Persistence.Client.Clients.Interfaces;
|
||||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||||
using DD.Persistence.Database.Entity;
|
using DD.Persistence.Database.Entity;
|
||||||
using DD.Persistence.Models.ChangeLog;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.Requests;
|
using DD.Persistence.Models.Requests;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using UuidExtensions;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
|
||||||
|
|
||||||
namespace DD.Persistence.IntegrationTests.Controllers;
|
namespace DD.Persistence.IntegrationTests.Controllers;
|
||||||
public class ChangeLogControllerTest : BaseIntegrationTest
|
public class ChangeLogControllerTest : BaseIntegrationTest
|
||||||
{
|
{
|
||||||
private readonly IChangeLogClient client;
|
private readonly IChangeLogClient client;
|
||||||
private readonly PaginationRequest paginationRequest;
|
private static readonly Random generatorRandomDigits = new();
|
||||||
private static readonly Random random = new();
|
|
||||||
|
|
||||||
public ChangeLogControllerTest(WebAppFactoryFixture factory) : base(factory)
|
public ChangeLogControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||||
{
|
{
|
||||||
@ -28,36 +25,54 @@ public class ChangeLogControllerTest : BaseIntegrationTest
|
|||||||
|
|
||||||
client = scope.ServiceProvider
|
client = scope.ServiceProvider
|
||||||
.GetRequiredService<IChangeLogClient>();
|
.GetRequiredService<IChangeLogClient>();
|
||||||
|
|
||||||
paginationRequest = new PaginationRequest()
|
|
||||||
{
|
|
||||||
Skip = 0,
|
|
||||||
Take = 10,
|
|
||||||
SortSettings = string.Empty,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ClearAndInsertRange()
|
public async Task ClearAndInsertRange_InEmptyDb()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
dbContext.CleanupDbSet<ChangeLog>();
|
||||||
|
|
||||||
|
var idDiscriminator = Guid.NewGuid();
|
||||||
|
var dtos = Generate(2);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.ClearAndAddRange(idDiscriminator, dtos, new CancellationToken());
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(2, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ClearAndInsertRange_InNotEmptyDb()
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var insertedCount = 10;
|
var insertedCount = 10;
|
||||||
var idDiscriminator = Guid.NewGuid();
|
var createdResult = CreateChangeLogItems(insertedCount, (-15, 15));
|
||||||
var otherOldDtos = GenerateChangeLogCreateRequest(10);
|
var idDiscriminator = createdResult.Item1;
|
||||||
await client.AddRange(Guid.NewGuid(), otherOldDtos, Guid.NewGuid().ToString(), CancellationToken.None);
|
var dtos = createdResult.Item2.Select(e => e.Adapt<ChangeLogValuesDto>());
|
||||||
|
|
||||||
var oldDtos = GenerateChangeLogCreateRequest(10);
|
|
||||||
await client.AddRange(idDiscriminator, oldDtos, Guid.NewGuid().ToString(), CancellationToken.None);
|
|
||||||
|
|
||||||
var newDtos = GenerateChangeLogCreateRequest(insertedCount);
|
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var result = await client.ClearAndAddRange(idDiscriminator, newDtos, "Добавление новых элементов и очистка старых", CancellationToken.None);
|
var result = await client.ClearAndAddRange(idDiscriminator, dtos, new CancellationToken());
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.Equal(insertedCount * 2, result);
|
Assert.Equal(insertedCount * 2, result);
|
||||||
var data = await client.GetByDate(idDiscriminator, DateTimeOffset.Now.AddSeconds(1), new PaginationRequest { Take = 10 * insertedCount }, CancellationToken.None);
|
}
|
||||||
Assert.Equal(insertedCount, data.Count);
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Add_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var count = 1;
|
||||||
|
var idDiscriminator = Guid.NewGuid();
|
||||||
|
var dtos = Generate(count);
|
||||||
|
var dto = dtos.FirstOrDefault()!;
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.Add(idDiscriminator, dto, new CancellationToken());
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(count, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -66,11 +81,10 @@ public class ChangeLogControllerTest : BaseIntegrationTest
|
|||||||
// arrange
|
// arrange
|
||||||
var count = 3;
|
var count = 3;
|
||||||
var idDiscriminator = Guid.NewGuid();
|
var idDiscriminator = Guid.NewGuid();
|
||||||
var dtos = GenerateChangeLogCreateRequest(count);
|
var dtos = Generate(count);
|
||||||
var comment = "Создаю 3 элемента";
|
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var result = await client.AddRange(idDiscriminator, dtos, comment, CancellationToken.None);
|
var result = await client.AddRange(idDiscriminator, dtos, new CancellationToken());
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.Equal(count, result);
|
Assert.Equal(count, result);
|
||||||
@ -80,41 +94,43 @@ public class ChangeLogControllerTest : BaseIntegrationTest
|
|||||||
public async Task Update_returns_success()
|
public async Task Update_returns_success()
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var begin = DateTimeOffset.UtcNow;
|
dbContext.CleanupDbSet<ChangeLog>();
|
||||||
var idDiscriminator = Guid.NewGuid();
|
|
||||||
var dtos = GenerateChangeLogCreateRequest(1);
|
|
||||||
var original = dtos.First();
|
|
||||||
var comment = "Создаю 1 элемент";
|
|
||||||
var result = await client.AddRange(idDiscriminator, [original], comment, CancellationToken.None);
|
|
||||||
|
|
||||||
var query = dbContext.Set<ChangeLog>()
|
var idDiscriminator = Guid.NewGuid();
|
||||||
.Include(x => x.CreatedCommit)
|
var dtos = Generate(1);
|
||||||
.Where(x => x.CreatedCommit.DiscriminatorId == idDiscriminator);
|
var dto = dtos.FirstOrDefault()!;
|
||||||
var entity = await query.FirstAsync();
|
var result = await client.Add(idDiscriminator, dto, new CancellationToken());
|
||||||
var modified = entity.Adapt<ChangeLogBaseDto>();
|
|
||||||
var key = modified.Value.First().Key;
|
var entity = dbContext.ChangeLog
|
||||||
modified.Value[key] = random.NextDouble();
|
.Where(x => x.IdDiscriminator == idDiscriminator)
|
||||||
|
.FirstOrDefault();
|
||||||
|
dto = entity.Adapt<ChangeLogValuesDto>();
|
||||||
|
|
||||||
// act
|
// act
|
||||||
comment = "Обновляю 1 элемент";
|
result = await client.Update(dto, new CancellationToken());
|
||||||
result = await client.UpdateRange(idDiscriminator, [modified], comment, CancellationToken.None);
|
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.Equal(2, result);
|
Assert.Equal(2, result);
|
||||||
|
|
||||||
var now = DateTimeOffset.UtcNow.AddSeconds(1);
|
var dateBegin = DateTimeOffset.UtcNow.AddDays(-1);
|
||||||
|
var dateEnd = DateTimeOffset.UtcNow.AddDays(1);
|
||||||
|
|
||||||
var changeLogResult = await client.GetChangeLogForInterval(idDiscriminator, begin, now, new CancellationToken());
|
var changeLogResult = await client.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, new CancellationToken());
|
||||||
Assert.NotNull(changeLogResult);
|
Assert.NotNull(changeLogResult);
|
||||||
|
|
||||||
var obsoleteDto = changeLogResult
|
var obsoleteDto = changeLogResult
|
||||||
.Where(e => e.Obsolete.HasValue)
|
.Where(e => e.Obsolete.HasValue)
|
||||||
.First();
|
.FirstOrDefault();
|
||||||
|
|
||||||
var activeDto = changeLogResult
|
var activeDto = changeLogResult
|
||||||
.Where(e => !e.Obsolete.HasValue)
|
.Where(e => !e.Obsolete.HasValue)
|
||||||
.OrderByDescending(e => e.Creation)
|
.FirstOrDefault();
|
||||||
.First();
|
|
||||||
|
if (obsoleteDto == null || activeDto == null)
|
||||||
|
{
|
||||||
|
Assert.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Assert.Equal(activeDto.Id, obsoleteDto.IdNext);
|
Assert.Equal(activeDto.Id, obsoleteDto.IdNext);
|
||||||
|
|
||||||
@ -123,321 +139,193 @@ public class ChangeLogControllerTest : BaseIntegrationTest
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task UpdateRange_returns_success()
|
public async Task UpdateRange_returns_success()
|
||||||
{
|
{
|
||||||
|
// arrange
|
||||||
var count = 2;
|
var count = 2;
|
||||||
var idDiscriminator = Guid.NewGuid();
|
var dtos = Generate(count);
|
||||||
var dtos = GenerateChangeLogCreateRequest(count);
|
var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray();
|
||||||
var comment = "Создаю 3 элемента";
|
dbContext.ChangeLog.AddRange(entities);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
dtos = entities.Select(c => new ChangeLogValuesDto()
|
||||||
|
{
|
||||||
|
Id = c.Id,
|
||||||
|
Value = c.Value
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var result = await client.AddRange(idDiscriminator, dtos, comment, CancellationToken.None);
|
var result = await client.UpdateRange(dtos, new CancellationToken());
|
||||||
var paginatedResult = await client.GetByDate(idDiscriminator, DateTimeOffset.UtcNow.AddDays(1), paginationRequest, CancellationToken.None);
|
|
||||||
// act
|
|
||||||
comment = "Обновляю 3 элемента";
|
|
||||||
result = await client.UpdateRange(idDiscriminator, paginatedResult.Items, comment, CancellationToken.None);
|
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.Equal(count * 2, result);
|
Assert.Equal(count * 2, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Delete_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var dtos = Generate(1);
|
||||||
|
var dto = dtos.FirstOrDefault()!;
|
||||||
|
var entity = dto.Adapt<ChangeLog>();
|
||||||
|
dbContext.ChangeLog.Add(entity);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.Delete(entity.Id, new CancellationToken());
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(1, result);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task DeleteRange_returns_success()
|
public async Task DeleteRange_returns_success()
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var insertedCount = 10;
|
var count = 10;
|
||||||
var idDiscriminator = Guid.NewGuid();
|
var dtos = Generate(count);
|
||||||
var otherOldDtos = GenerateChangeLogCreateRequest(10);
|
var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray();
|
||||||
await client.AddRange(Guid.NewGuid(), otherOldDtos, Guid.NewGuid().ToString(), CancellationToken.None);
|
dbContext.ChangeLog.AddRange(entities);
|
||||||
|
dbContext.SaveChanges();
|
||||||
var oldDtos = GenerateChangeLogCreateRequest(10);
|
|
||||||
await client.AddRange(idDiscriminator, oldDtos, Guid.NewGuid().ToString(), CancellationToken.None);
|
|
||||||
var existing = await client.GetByDate(idDiscriminator, DateTimeOffset.UtcNow.AddSeconds(1), new PaginationRequest { Take= 100*insertedCount}, CancellationToken.None);
|
|
||||||
var ids = existing.Items.Select(e => e.Id);
|
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var result = await client.DeleteRange(idDiscriminator, ids, "Удаление нескольких записей", CancellationToken.None);
|
var ids = entities.Select(e => e.Id);
|
||||||
|
var result = await client.DeleteRange(ids, new CancellationToken());
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.Equal(insertedCount, result);
|
Assert.Equal(count, result);
|
||||||
var items = await dbContext.Set<ChangeLog>()
|
|
||||||
.Where(e=> ids.Contains(e.Id))
|
|
||||||
.ToArrayAsync(CancellationToken.None);
|
|
||||||
|
|
||||||
Assert.Equal(insertedCount, items.Count());
|
|
||||||
Assert.All(items, i => Assert.NotNull(i.IdObsoletedCommit));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ChangeLogCommit GenerateCommit(Guid discriminatorId, DateTimeOffset creation)
|
|
||||||
=> GenerateCommit(discriminatorId, Guid.NewGuid(), creation);
|
|
||||||
|
|
||||||
static ChangeLogCommit GenerateCommit(Guid discriminatorId, Guid userId, DateTimeOffset creation)
|
|
||||||
=> new ChangeLogCommit
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
IdAuthor = userId,
|
|
||||||
Creation = creation,
|
|
||||||
Comment = Guid.NewGuid().ToString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
static IEnumerable<ChangeLog> GenerateChangeLog(int count, ChangeLogCommit createCommit)
|
|
||||||
=> GenerateChangeLog(count, createCommit, null, null);
|
|
||||||
|
|
||||||
static IEnumerable<ChangeLog> GenerateChangeLog(int count, ChangeLogCommit createCommit, ChangeLogCommit? obsoleteCommit, Guid? idNext)
|
|
||||||
{
|
|
||||||
for (var i = 0; i< count; i++)
|
|
||||||
{
|
|
||||||
yield return new ChangeLog
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
CreatedCommit = createCommit,
|
|
||||||
IdCreatedCommit = createCommit.Id,
|
|
||||||
ObsoletedCommit = obsoleteCommit,
|
|
||||||
IdObsoletedCommit = obsoleteCommit?.Id,
|
|
||||||
IdNext = idNext,
|
|
||||||
Value = new Dictionary<string, object> { { "Guid.NewGuid()", random.NextDouble() } }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static ChangeLogCommit GenerateCommitWithNewData(Guid discriminatorId, DateTimeOffset creation, int count)
|
|
||||||
{
|
|
||||||
var commit = GenerateCommit(discriminatorId, creation);
|
|
||||||
commit.ChangeLogCreatedItems = GenerateChangeLog(count, commit).ToList();
|
|
||||||
return commit;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<ChangeLogCommit> SeedCommitWithNewData(Guid discriminatorId, DateTimeOffset creation, int count)
|
|
||||||
{
|
|
||||||
var data = GenerateCommitWithNewData(discriminatorId, creation, count);
|
|
||||||
dbContext.Set<ChangeLogCommit>().Add(data);
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetDatesRange_returns_success()
|
public async Task GetDatesRange_returns_success()
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var dateMin = DateTimeOffset.UtcNow.AddDays(-1);
|
var changeLogItems = CreateChangeLogItems(3, (-15, 15));
|
||||||
var dateMax = DateTimeOffset.UtcNow;
|
var idDiscriminator = changeLogItems.Item1;
|
||||||
var dateMid = dateMin + 0.5 * (dateMax - dateMin);
|
var entities = changeLogItems.Item2.OrderBy(e => e.Creation);
|
||||||
var idDiscriminator = Guid.NewGuid();
|
|
||||||
var idOtherDiscriminator = Guid.NewGuid();
|
|
||||||
var count = 15;
|
|
||||||
|
|
||||||
await SeedCommitWithNewData(idDiscriminator, dateMin, count);
|
|
||||||
await SeedCommitWithNewData(idDiscriminator, dateMid, count);
|
|
||||||
await SeedCommitWithNewData(idDiscriminator, dateMax, count);
|
|
||||||
await SeedCommitWithNewData(idOtherDiscriminator, dateMin.AddDays(-1), count);
|
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var result = await client.GetDatesRange(idDiscriminator, CancellationToken.None);
|
var result = await client.GetDatesRange(idDiscriminator, new CancellationToken());
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal(dateMin, result.From, TimeSpan.FromSeconds(1));
|
|
||||||
Assert.Equal(dateMax, result.To, TimeSpan.FromSeconds(1));
|
var minDate = entities.First().Creation;
|
||||||
|
var maxDate = entities.Last().Creation;
|
||||||
|
|
||||||
|
var expectedMinDate = minDate.ToUniversalTime().ToString();
|
||||||
|
var actualMinDate = result.From.ToUniversalTime().ToString();
|
||||||
|
Assert.Equal(expectedMinDate, actualMinDate);
|
||||||
|
|
||||||
|
var expectedMaxDate = maxDate.ToUniversalTime().ToString();
|
||||||
|
var actualMaxDate = result.To.ToUniversalTime().ToString();
|
||||||
|
Assert.Equal(expectedMaxDate, actualMaxDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetByDate_returns_success()
|
public async Task GetByDate_returns_success()
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var dbSetCommit = dbContext.Set<ChangeLogCommit>();
|
dbContext.CleanupDbSet<ChangeLog>();
|
||||||
var dbSet = dbContext.Set<ChangeLog>();
|
|
||||||
|
|
||||||
var idDiscriminator = Guid.NewGuid();
|
//создаем записи
|
||||||
var date = DateTimeOffset.UtcNow;
|
var count = 5;
|
||||||
var expected = 10;
|
var changeLogItems = CreateChangeLogItems(count, (-15, 15));
|
||||||
var skip = 2;
|
var idDiscriminator = changeLogItems.Item1;
|
||||||
var take = skip + 2;
|
var entities = changeLogItems.Item2;
|
||||||
var count = expected + take;
|
|
||||||
var commit = GenerateCommit(idDiscriminator, date);
|
|
||||||
dbSetCommit.Add(commit);
|
|
||||||
var commitData = GenerateChangeLog(count, commit);
|
|
||||||
dbSet.AddRange(commitData);
|
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync(CancellationToken.None);
|
//удаляем все созданные записи за исключением первой и второй
|
||||||
var dataWithIds = await dbSet
|
//даты 2-х оставшихся записей должны вернуться в методе GetByDate
|
||||||
.Where(e=>e.CreatedCommit.DiscriminatorId == idDiscriminator)
|
var ids = entities.Select(e => e.Id);
|
||||||
.AsNoTracking()
|
var idsToDelete = ids.Skip(2);
|
||||||
.ToArrayAsync(CancellationToken.None);
|
|
||||||
|
|
||||||
var itemsToDelete = dataWithIds.Skip(skip).Take(take);
|
var deletedCount = await client.DeleteRange(idsToDelete, new CancellationToken());
|
||||||
var idsToDelete = itemsToDelete.Select(x => x.Id).ToArray();
|
|
||||||
|
|
||||||
// act
|
var paginationRequest = new PaginationRequest()
|
||||||
var deletedCount = await client.DeleteRange(idDiscriminator, idsToDelete, "Удаление нескольких записей", CancellationToken.None);
|
{
|
||||||
|
Skip = 0,
|
||||||
|
Take = 10,
|
||||||
|
SortSettings = String.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
// assert
|
var moment = DateTimeOffset.UtcNow.AddDays(16);
|
||||||
var moment = DateTimeOffset.UtcNow.AddSeconds(1);
|
var result = await client.GetByDate(idDiscriminator, moment, paginationRequest, new CancellationToken());
|
||||||
var result = await client.GetByDate(idDiscriminator, moment, new() { Take = count *100}, new CancellationToken());
|
|
||||||
|
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal(expected, result.Items.Count());
|
|
||||||
|
var restEntities = entities.Where(e => !idsToDelete.Contains(e.Id));
|
||||||
|
Assert.Equal(restEntities.Count(), result.Count);
|
||||||
|
|
||||||
|
var actualIds = restEntities.Select(e => e.Id);
|
||||||
|
var expectedIds = result.Items.Select(e => e.Id);
|
||||||
|
Assert.Equivalent(expectedIds, actualIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task GetChangeLogForInterval_returns_success()
|
[InlineData(5, -15, 15, -20, 20, 10)]
|
||||||
|
[InlineData(5, -15, -10, -16, -9, 5)]
|
||||||
|
public async Task GetChangeLogForInterval_returns_success(
|
||||||
|
int insertedCount,
|
||||||
|
int daysBeforeNowChangeLog,
|
||||||
|
int daysAfterNowChangeLog,
|
||||||
|
int daysBeforeNowFilter,
|
||||||
|
int daysAfterNowFilter,
|
||||||
|
int changeLogCount)
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var dateMin = DateTimeOffset.UtcNow.AddDays(- 11);
|
dbContext.CleanupDbSet<ChangeLog>();
|
||||||
var dateMax = DateTimeOffset.UtcNow;
|
|
||||||
var dateMid = dateMin + 0.5 * (dateMax - dateMin);
|
|
||||||
var idDiscriminator = Guid.NewGuid();
|
|
||||||
var idOtherDiscriminator = Guid.NewGuid();
|
|
||||||
var count = 17;
|
|
||||||
|
|
||||||
var commitMin = await SeedCommitWithNewData(idDiscriminator, dateMin, count);
|
|
||||||
var commitMid = await SeedCommitWithNewData(idDiscriminator, dateMid, count);
|
|
||||||
await SeedCommitWithNewData(idDiscriminator, dateMax, count);
|
|
||||||
await SeedCommitWithNewData(idOtherDiscriminator, dateMin.AddDays(-1), count);
|
|
||||||
|
|
||||||
var dateBegin = commitMin.Creation;
|
|
||||||
var dateEnd = commitMid.Creation.AddSeconds(1);
|
|
||||||
|
|
||||||
var expectedCount = commitMin.ChangeLogCreatedItems.Count()
|
|
||||||
+ commitMid.ChangeLogCreatedItems.Count();
|
|
||||||
//act
|
|
||||||
|
|
||||||
var result = await client.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
Assert.Equal(expectedCount, result.Count());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetStatistics_returns_success()
|
|
||||||
{
|
|
||||||
// arrange
|
|
||||||
var discriminatorId = Guid.NewGuid();
|
|
||||||
//создаем записи
|
//создаем записи
|
||||||
var date0 = DateTimeOffset.UtcNow.AddDays(-17);
|
var count = insertedCount;
|
||||||
var day0 = new DateOnly(date0.Year, date0.Month, date0.Day);
|
var daysRange = (daysBeforeNowChangeLog, daysAfterNowChangeLog);
|
||||||
var inserted0 = 17;
|
var changeLogItems = CreateChangeLogItems(count, daysRange);
|
||||||
var commit0 = await SeedCommitWithNewData(discriminatorId, date0, inserted0);
|
var idDiscriminator = changeLogItems.Item1;
|
||||||
|
var entities = changeLogItems.Item2;
|
||||||
|
|
||||||
// создаем еще записи
|
var dtos = entities.Select(e => e.Adapt<ChangeLogValuesDto>()).ToArray();
|
||||||
var date1 = DateTimeOffset.UtcNow.AddDays(-7);
|
await client.UpdateRange(dtos, new CancellationToken());
|
||||||
var day1 = new DateOnly(date1.Year, date1.Month, date1.Day);
|
|
||||||
var inserted1 = inserted0 + 5;
|
|
||||||
var commit1 = await SeedCommitWithNewData(discriminatorId, date1, inserted1);
|
|
||||||
|
|
||||||
// создаем еще записи с новым дискриминатором
|
|
||||||
await SeedCommitWithNewData(Guid.NewGuid(), date1, 17);
|
|
||||||
|
|
||||||
// обновим
|
|
||||||
var date2 = DateTimeOffset.UtcNow;
|
|
||||||
var day2 = new DateOnly(date2.Year, date2.Month, date2.Day);
|
|
||||||
var updated = commit0.ChangeLogCreatedItems.Select(i => new ChangeLogBaseDto { Id = i.Id, Value = i.Value });
|
|
||||||
await client.UpdateRange(commit0.DiscriminatorId, updated, Guid.NewGuid().ToString(), CancellationToken.None);
|
|
||||||
|
|
||||||
// удалим из коммита
|
|
||||||
var idToDelete = commit1.ChangeLogCreatedItems.First().Id;
|
|
||||||
await client.DeleteRange(commit1.DiscriminatorId, [idToDelete], Guid.NewGuid().ToString(), CancellationToken.None);
|
|
||||||
|
|
||||||
//act
|
//act
|
||||||
var request = new ChangeLogQuery()
|
var dateBegin = DateTimeOffset.UtcNow.AddDays(daysBeforeNowFilter);
|
||||||
{
|
var dateEnd = DateTimeOffset.UtcNow.AddDays(daysAfterNowFilter);
|
||||||
DiscriminatorId = commit0.DiscriminatorId,
|
var result = await client.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, new CancellationToken());
|
||||||
UserId = null
|
|
||||||
};
|
|
||||||
var statistics = await client.GetStatistics(request, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
Assert.Equal(3, statistics.Count());
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal(changeLogCount, result.Count());
|
||||||
var stat0 = statistics.First(s => s.Date == day0);
|
|
||||||
Assert.Equal(inserted0, stat0.CreatedChangeLogCount);
|
|
||||||
Assert.Equal(0, stat0.ObsoletedCount);
|
|
||||||
Assert.Equal(1, stat0.CommitCount);
|
|
||||||
|
|
||||||
var stat1 = statistics.First(s => s.Date == day1);
|
|
||||||
Assert.Equal(inserted1, stat1.CreatedChangeLogCount);
|
|
||||||
Assert.Equal(0, stat1.ObsoletedCount);
|
|
||||||
Assert.Equal(1, stat1.CommitCount);
|
|
||||||
|
|
||||||
var stat2 = statistics.First(s => s.Date == day2);
|
|
||||||
Assert.Equal(inserted0, stat2.CreatedChangeLogCount);
|
|
||||||
Assert.Equal(inserted0 + 1, stat2.ObsoletedCount);
|
|
||||||
Assert.Equal(2, stat2.CommitCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetHistory_returns_success()
|
|
||||||
{
|
|
||||||
// arrange
|
|
||||||
var discriminatorId = Guid.NewGuid();
|
|
||||||
// создаем записи
|
|
||||||
var date0 = DateTimeOffset.UtcNow.AddDays(-17);
|
|
||||||
var inserted0 = 17;
|
|
||||||
var commit0 = await SeedCommitWithNewData(discriminatorId, date0, inserted0);
|
|
||||||
|
|
||||||
// обновим
|
private static IEnumerable<ChangeLogValuesDto> Generate(int count)
|
||||||
var date2 = DateTimeOffset.UtcNow;
|
|
||||||
var updated = commit0.ChangeLogCreatedItems.Skip(1).Select(i => new ChangeLogBaseDto { Id = i.Id, Value = i.Value });
|
|
||||||
await client.UpdateRange(commit0.DiscriminatorId, updated, Guid.NewGuid().ToString(), CancellationToken.None);
|
|
||||||
|
|
||||||
// удалим из коммита
|
|
||||||
var idToDelete = commit0.ChangeLogCreatedItems.First().Id;
|
|
||||||
await client.DeleteRange(commit0.DiscriminatorId, [idToDelete], Guid.NewGuid().ToString(), CancellationToken.None);
|
|
||||||
|
|
||||||
var request = new ChangeLogQuery()
|
|
||||||
{
|
|
||||||
DiscriminatorId = commit0.DiscriminatorId,
|
|
||||||
UserId = null
|
|
||||||
};
|
|
||||||
var history = await client.GetHistory(request, CancellationToken.None);
|
|
||||||
Assert.Equal(3, history.Count());
|
|
||||||
|
|
||||||
var items = history.OrderBy(e => e.Creation);
|
|
||||||
var firstItem = items.First();
|
|
||||||
Assert.Equal(date0.DateTime.ToString(), firstItem.Creation.DateTime.ToString());
|
|
||||||
Assert.Equal(inserted0, firstItem.ChangeLogCreatedItems.Count());
|
|
||||||
Assert.Empty(firstItem.ChangeLogObsoletedItems);
|
|
||||||
|
|
||||||
var middleItem = items.Skip(1).Take(1).First();
|
|
||||||
Assert.Equal(date2.DateTime.ToString(), middleItem.Creation.DateTime.ToString());
|
|
||||||
Assert.Equal(updated.Count(), middleItem.ChangeLogObsoletedItems.Count());
|
|
||||||
Assert.Equal(updated.Count(), middleItem.ChangeLogCreatedItems.Count());
|
|
||||||
|
|
||||||
var lastItem = items.Last();
|
|
||||||
Assert.Equal(date2.DateTime.ToString(), lastItem.Creation.DateTime.ToString());
|
|
||||||
Assert.Single(lastItem.ChangeLogObsoletedItems);
|
|
||||||
|
|
||||||
//тест случая, когда данных за указанный период нет
|
|
||||||
request = new ChangeLogQuery()
|
|
||||||
{
|
|
||||||
DiscriminatorId = commit0.DiscriminatorId,
|
|
||||||
UserId = null,
|
|
||||||
GeDate = DateTimeOffset.UtcNow.AddMinutes(1),
|
|
||||||
LeDate = DateTimeOffset.UtcNow.AddDays(10),
|
|
||||||
};
|
|
||||||
history = await client.GetHistory(request, CancellationToken.None);
|
|
||||||
Assert.Empty(history);
|
|
||||||
|
|
||||||
//тест случая, когда данные за указанный период есть
|
|
||||||
request = new ChangeLogQuery()
|
|
||||||
{
|
|
||||||
DiscriminatorId = commit0.DiscriminatorId,
|
|
||||||
UserId = null,
|
|
||||||
GeDate = DateTimeOffset.UtcNow.AddDays(-17).AddMinutes(-10),
|
|
||||||
LeDate = DateTimeOffset.UtcNow.AddDays(-1),
|
|
||||||
};
|
|
||||||
history = await client.GetHistory(request, CancellationToken.None);
|
|
||||||
Assert.Single(history);
|
|
||||||
|
|
||||||
var createdChangeLogs = history.First().ChangeLogCreatedItems;
|
|
||||||
Assert.Equal(17, createdChangeLogs.Count());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<IDictionary<string, object>> GenerateChangeLogCreateRequest(int count)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
yield return new Dictionary<string, object>()
|
yield return new ChangeLogValuesDto()
|
||||||
{
|
{
|
||||||
{ "Key", random.NextDouble() }
|
Value = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "Key", 1 }
|
||||||
|
},
|
||||||
|
Id = Guid.NewGuid()
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (Guid, ChangeLog[]) CreateChangeLogItems(int count, (int, int) daysRange)
|
||||||
|
{
|
||||||
|
var minDayCount = daysRange.Item1;
|
||||||
|
var maxDayCount = daysRange.Item2;
|
||||||
|
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
var dtos = Generate(count);
|
||||||
|
var entities = dtos.Select(d =>
|
||||||
|
{
|
||||||
|
var entity = d.Adapt<ChangeLog>();
|
||||||
|
entity.IdDiscriminator = idDiscriminator;
|
||||||
|
entity.Creation = DateTimeOffset.UtcNow.AddDays(generatorRandomDigits.Next(minDayCount, maxDayCount));
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}).ToArray();
|
||||||
|
dbContext.ChangeLog.AddRange(entities);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
return (idDiscriminator, entities);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using DD.Persistence.Client;
|
using DD.Persistence.Client;
|
||||||
|
using DD.Persistence.Client.Clients;
|
||||||
using DD.Persistence.Client.Clients.Interfaces;
|
using DD.Persistence.Client.Clients.Interfaces;
|
||||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||||
using DD.Persistence.Client.Clients.Mapping.Clients;
|
|
||||||
using DD.Persistence.Database.Entity;
|
using DD.Persistence.Database.Entity;
|
||||||
using DD.Persistence.Models.Configurations;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -13,14 +13,17 @@ namespace DD.Persistence.IntegrationTests.Controllers
|
|||||||
public class SetpointControllerTest : BaseIntegrationTest
|
public class SetpointControllerTest : BaseIntegrationTest
|
||||||
{
|
{
|
||||||
private readonly ISetpointClient setpointClient;
|
private readonly ISetpointClient setpointClient;
|
||||||
|
private readonly SetpointConfigStorage configStorage;
|
||||||
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
|
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||||
{
|
{
|
||||||
var refitClientFactory = scope.ServiceProvider
|
var refitClientFactory = scope.ServiceProvider
|
||||||
.GetRequiredService<IRefitClientFactory<IRefitSetpointClient>>();
|
.GetRequiredService<IRefitClientFactory<IRefitSetpointClient>>();
|
||||||
|
var logger = scope.ServiceProvider.GetRequiredService<ILogger<SetpointClient>>();
|
||||||
|
|
||||||
setpointClient = scope.ServiceProvider
|
setpointClient = scope.ServiceProvider
|
||||||
.GetRequiredService<ISetpointClient>();
|
.GetRequiredService<ISetpointClient>();
|
||||||
|
|
||||||
|
configStorage = (SetpointConfigStorage)scope.ServiceProvider.GetRequiredService<ISetpointConfigStorage>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -29,16 +32,12 @@ namespace DD.Persistence.IntegrationTests.Controllers
|
|||||||
{
|
{
|
||||||
var id = Guid.Parse("e0fcad22-1761-476e-a729-a3c59d51ba41");
|
var id = Guid.Parse("e0fcad22-1761-476e-a729-a3c59d51ba41");
|
||||||
|
|
||||||
var config = new MappingConfig();
|
configStorage.AddOrReplace(id, typeof(float));
|
||||||
config[id] = typeof(float);
|
|
||||||
|
|
||||||
var setpointMapper = new SetpointMappingClient(setpointClient, config);
|
|
||||||
|
|
||||||
|
|
||||||
await setpointClient.Add(id, 48.3f, CancellationToken.None);
|
await setpointClient.Add(id, 48.3f, CancellationToken.None);
|
||||||
|
|
||||||
//act
|
//act
|
||||||
var response = await setpointMapper.GetCurrent([id], CancellationToken.None);
|
var response = await setpointClient.GetCurrent([id], CancellationToken.None);
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
Assert.NotNull(response);
|
Assert.NotNull(response);
|
||||||
|
@ -3,6 +3,7 @@ using DD.Persistence.Client.Clients;
|
|||||||
using DD.Persistence.Client.Clients.Interfaces;
|
using DD.Persistence.Client.Clients.Interfaces;
|
||||||
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
using DD.Persistence.Client.Clients.Interfaces.Refit;
|
||||||
using DD.Persistence.Database.Entity;
|
using DD.Persistence.Database.Entity;
|
||||||
|
using DD.Persistence.Extensions;
|
||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@ -38,7 +39,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Get_returns_BadRequest()
|
public async Task Get_returns_success()
|
||||||
{
|
{
|
||||||
//arrange
|
//arrange
|
||||||
Cleanup();
|
Cleanup();
|
||||||
@ -49,18 +50,11 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
|
|||||||
var secondDiscriminatorId = Guid.NewGuid();
|
var secondDiscriminatorId = Guid.NewGuid();
|
||||||
discriminatorIds.Append(secondDiscriminatorId);
|
discriminatorIds.Append(secondDiscriminatorId);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//act
|
//act
|
||||||
var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId], null, null, null, 0, 1, CancellationToken.None);
|
var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId], null, null, 0, 1, CancellationToken.None);
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
var expectedMessage = $"На сервере произошла ошибка, в результате которой он не может успешно обработать запрос";
|
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
Assert.Equal(expectedMessage, ex.Message);
|
Assert.Null(response);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -77,25 +71,22 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
|
|||||||
|
|
||||||
var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1);
|
var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1);
|
||||||
var columnNames = new List<string>() { "A", "C" };
|
var columnNames = new List<string>() { "A", "C" };
|
||||||
var skip = 0;
|
var skip = 2;
|
||||||
var take = 6; // Ровно столько значений будет удовлетворять фильтру (\"A\">3) (для одного дискриминатора)
|
var take = 16;
|
||||||
|
|
||||||
var customFilter = "(\"A\">3)";
|
|
||||||
|
|
||||||
var dtos = (await AddRange(firstDiscriminatorId)).ToList();
|
var dtos = (await AddRange(firstDiscriminatorId)).ToList();
|
||||||
dtos.AddRange(await AddRange(secondDiscriminatorId));
|
dtos.AddRange(await AddRange(secondDiscriminatorId));
|
||||||
|
|
||||||
//act
|
//act
|
||||||
var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId],
|
var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId],
|
||||||
timestampBegin, customFilter, columnNames, skip, take, CancellationToken.None);
|
timestampBegin, columnNames, skip, take, CancellationToken.None);
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
Assert.NotNull(response);
|
Assert.NotNull(response);
|
||||||
Assert.NotEmpty(response);
|
Assert.NotEmpty(response);
|
||||||
|
|
||||||
var expectedCount = take * 2;
|
|
||||||
var actualCount = response.Count();
|
var actualCount = response.Count();
|
||||||
Assert.Equal(expectedCount, actualCount);
|
Assert.Equal(take, actualCount);
|
||||||
|
|
||||||
var actualColumnNames = response.SelectMany(e => e.Values.Keys).Distinct().ToList();
|
var actualColumnNames = response.SelectMany(e => e.Values.Keys).Distinct().ToList();
|
||||||
Assert.Equal(columnNames, actualColumnNames);
|
Assert.Equal(columnNames, actualColumnNames);
|
||||||
@ -388,7 +379,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
|
|||||||
var response = await timestampedValuesClient.AddRange(discriminatorId, generatedDtos, CancellationToken.None);
|
var response = await timestampedValuesClient.AddRange(discriminatorId, generatedDtos, CancellationToken.None);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
//Assert.Equal(generatedDtos.Count(), response);
|
Assert.Equal(generatedDtos.Count(), response);
|
||||||
|
|
||||||
return generatedDtos;
|
return generatedDtos;
|
||||||
}
|
}
|
||||||
@ -416,12 +407,8 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
|
|||||||
|
|
||||||
private void Cleanup()
|
private void Cleanup()
|
||||||
{
|
{
|
||||||
foreach (var item in discriminatorIds)
|
|
||||||
{
|
|
||||||
memoryCache.Remove(item);
|
|
||||||
}
|
|
||||||
discriminatorIds = [];
|
discriminatorIds = [];
|
||||||
dbContext.CleanupDbSet<TimestampedValues>();
|
dbContext.CleanupDbSet<TimestampedValues>();
|
||||||
dbContext.CleanupDbSet<SchemeProperty>();
|
dbContext.CleanupDbSet<DataScheme>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using DD.Persistence.Client.Helpers;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace DD.Persistence.IntegrationTests
|
namespace DD.Persistence.IntegrationTests
|
||||||
{
|
{
|
||||||
|
@ -5,9 +5,13 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using DD.Persistence.API;
|
||||||
using DD.Persistence.Client;
|
using DD.Persistence.Client;
|
||||||
using DD.Persistence.Database.Model;
|
using DD.Persistence.Database.Model;
|
||||||
using DD.Persistence.App;
|
using DD.Persistence.App;
|
||||||
|
using DD.Persistence.Client.Helpers;
|
||||||
|
using DD.Persistence.Factories;
|
||||||
|
using System.Net;
|
||||||
using DD.Persistence.Database.Postgres.Extensions;
|
using DD.Persistence.Database.Postgres.Extensions;
|
||||||
|
|
||||||
namespace DD.Persistence.IntegrationTests;
|
namespace DD.Persistence.IntegrationTests;
|
||||||
@ -20,9 +24,10 @@ public class WebAppFactoryFixture : WebApplicationFactory<Program>
|
|||||||
builder.ConfigureAppConfiguration((hostingContext, config) =>
|
builder.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
{
|
{
|
||||||
config.AddJsonFile("appsettings.Tests.json");
|
config.AddJsonFile("appsettings.Tests.json");
|
||||||
var configurationRoot = config.Build();
|
|
||||||
var dbConnection = configurationRoot.GetSection("DbConnection").Get<DbConnection>()!;
|
var dbConnection = config.Build().GetSection("DbConnection").Get<DbConnection>()!;
|
||||||
connectionString = dbConnection.GetConnectionString();
|
connectionString = dbConnection.GetConnectionString();
|
||||||
|
//connectionString = "Host=postgres;Port=5442;Username=postgres;Password=postgres;Database=persistence";
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.ConfigureServices(services =>
|
builder.ConfigureServices(services =>
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
namespace DD.Persistence.Models.ChangeLog;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Модель для создания коммита
|
|
||||||
/// </summary>
|
|
||||||
public class ChangeLogCommitCreateRequest
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// DiscriminatorId
|
|
||||||
/// </summary>
|
|
||||||
public Guid DiscriminatorId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Пользователь, совершающий коммит
|
|
||||||
/// </summary>
|
|
||||||
public Guid IdAuthor { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Комментарий
|
|
||||||
/// </summary>
|
|
||||||
public string Comment { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ctor
|
|
||||||
/// </summary>
|
|
||||||
public ChangeLogCommitCreateRequest()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ctor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="discriminatorId"></param>
|
|
||||||
/// <param name="idAuthor"></param>
|
|
||||||
/// <param name="comment"></param>
|
|
||||||
public ChangeLogCommitCreateRequest(Guid discriminatorId, Guid idAuthor, string comment)
|
|
||||||
{
|
|
||||||
DiscriminatorId = discriminatorId;
|
|
||||||
IdAuthor = idAuthor;
|
|
||||||
Comment = comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
namespace DD.Persistence.Models.ChangeLog;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Модель коммита с изменениями
|
|
||||||
/// </summary>
|
|
||||||
public class ChangeLogCommitDto : ChangeLogCommitCreateRequest
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Id
|
|
||||||
/// </summary>
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Дата создания
|
|
||||||
/// </summary>
|
|
||||||
public DateTimeOffset Creation { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Список созданных записей коммита
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<ChangeLogDto> ChangeLogCreatedItems { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Список устаревших записей коммита
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<ChangeLogDto> ChangeLogObsoletedItems { get; set; } = null!;
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
namespace DD.Persistence.Models.ChangeLog;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Запрос, используемый для получения данных по журналу операций
|
|
||||||
/// </summary>
|
|
||||||
public class ChangeLogQuery
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Дискриминатор задачи
|
|
||||||
/// </summary>
|
|
||||||
public Guid DiscriminatorId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Пользователь
|
|
||||||
/// </summary>
|
|
||||||
public Guid? UserId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Больше или равно указанной даты
|
|
||||||
/// </summary>
|
|
||||||
public DateTimeOffset? GeDate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Меньше или равно указанной даты
|
|
||||||
/// </summary>
|
|
||||||
public DateTimeOffset? LeDate { get; set; }
|
|
||||||
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
namespace DD.Persistence.Models.ChangeLog;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Модель, необходимая для отображения статистики по журналу изменений
|
|
||||||
/// </summary>
|
|
||||||
public class ChangeLogStatisticsDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Дата изменений
|
|
||||||
/// </summary>
|
|
||||||
public DateOnly Date { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Количество созданных элементов журнала изменений
|
|
||||||
/// </summary>
|
|
||||||
public int CreatedChangeLogCount { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Количество устаревших элементов журнала изменений
|
|
||||||
/// </summary>
|
|
||||||
public int ObsoletedCount { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Количество коммитов
|
|
||||||
/// </summary>
|
|
||||||
public int CommitCount { get; set; }
|
|
||||||
}
|
|
@ -1,14 +1,19 @@
|
|||||||
namespace DD.Persistence.Models.ChangeLog;
|
namespace DD.Persistence.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Часть записи описывающая изменение
|
/// Часть записи описывающая изменение
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChangeLogDto: ChangeLogBaseDto
|
public class ChangeLogDto
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ключ записи
|
||||||
|
/// </summary>
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Создатель записи
|
/// Создатель записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid? IdAuthor { get; set; }
|
public Guid IdAuthor { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Пользователь, изменивший запись
|
/// Пользователь, изменивший запись
|
||||||
@ -18,7 +23,7 @@ public class ChangeLogDto: ChangeLogBaseDto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата создания
|
/// Дата создания
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTimeOffset? Creation { get; set; }
|
public DateTimeOffset Creation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата устаревания
|
/// Дата устаревания
|
||||||
@ -29,4 +34,9 @@ public class ChangeLogDto: ChangeLogBaseDto
|
|||||||
/// Ключ заменившей записи
|
/// Ключ заменившей записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid? IdNext { get; set; }
|
public Guid? IdNext { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Объект записи
|
||||||
|
/// </summary>
|
||||||
|
public ChangeLogValuesDto Value { get; set; } = default!;
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
namespace DD.Persistence.Models.ChangeLog;
|
namespace DD.Persistence.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dto для хранения записей
|
/// Dto для хранения записей, содержащих начальную и конечную глубину забоя, а также секцию
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChangeLogBaseDto
|
public class ChangeLogValuesDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ключ записи
|
/// Ключ записи
|
@ -1,4 +0,0 @@
|
|||||||
namespace DD.Persistence.Models.Configurations;
|
|
||||||
public class MappingConfig : Dictionary<Guid, Type>
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,11 +1,9 @@
|
|||||||
using System.Collections;
|
namespace DD.Persistence.Models;
|
||||||
|
|
||||||
namespace DD.Persistence.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Схема для набора данных
|
/// Схема для набора данных
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DataSchemeDto : IEnumerable<SchemePropertyDto>, IEquatable<IEnumerable<SchemePropertyDto>>
|
public class DataSchemeDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дискриминатор
|
/// Дискриминатор
|
||||||
@ -13,30 +11,7 @@ public class DataSchemeDto : IEnumerable<SchemePropertyDto>, IEquatable<IEnumera
|
|||||||
public Guid DiscriminatorId { get; set; }
|
public Guid DiscriminatorId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Поля
|
/// Наименования полей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IEnumerable<SchemePropertyDto> Properties { get; } = [];
|
public string[] PropNames { get; set; } = [];
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public DataSchemeDto(Guid discriminatorId, IEnumerable<SchemePropertyDto> Properties)
|
|
||||||
{
|
|
||||||
DiscriminatorId = discriminatorId;
|
|
||||||
this.Properties = Properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IEnumerator<SchemePropertyDto> GetEnumerator()
|
|
||||||
=> Properties.GetEnumerator();
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
=> GetEnumerator();
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool Equals(IEnumerable<SchemePropertyDto>? otherProperties)
|
|
||||||
{
|
|
||||||
if (otherProperties is null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return Properties.SequenceEqual(otherProperties);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Индексируемого поле из схемы для набора данных
|
|
||||||
/// </summary>
|
|
||||||
public class SchemePropertyDto : IEquatable<SchemePropertyDto>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Индекс поля
|
|
||||||
/// </summary>
|
|
||||||
public required int Index { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Наименование индексируемого поля
|
|
||||||
/// </summary>
|
|
||||||
public required string PropertyName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тип индексируемого поля
|
|
||||||
/// </summary>
|
|
||||||
public required JsonValueKind PropertyKind { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool Equals(SchemePropertyDto? other)
|
|
||||||
{
|
|
||||||
return Index == other?.Index && PropertyName == other?.PropertyName && PropertyKind == other?.PropertyKind;
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,11 +7,6 @@ namespace DD.Persistence.Models;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class TimestampedValuesDto : ITimestampAbstractDto
|
public class TimestampedValuesDto : ITimestampAbstractDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Дискриминатор
|
|
||||||
/// </summary>
|
|
||||||
public Guid DiscriminatorId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Временная отметка
|
/// Временная отметка
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Класс, описывающий пользователя
|
|
||||||
/// </summary>
|
|
||||||
public class UserDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Идентификатор пользователя
|
|
||||||
/// </summary>
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Имя пользователя для отображения
|
|
||||||
/// </summary>
|
|
||||||
public required string DisplayName { get; set; }
|
|
||||||
}
|
|
@ -11,8 +11,8 @@
|
|||||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||||
<PackageReference Include="Shouldly" Version="4.2.1" />
|
<PackageReference Include="Shouldly" Version="4.2.1" />
|
||||||
<PackageReference Include="Testcontainers" Version="4.2.0" />
|
<PackageReference Include="Testcontainers" Version="4.1.0" />
|
||||||
<PackageReference Include="Testcontainers.PostgreSql" Version="4.2.0" />
|
<PackageReference Include="Testcontainers.PostgreSql" Version="4.1.0" />
|
||||||
<PackageReference Include="xunit" Version="2.9.2" />
|
<PackageReference Include="xunit" Version="2.9.2" />
|
||||||
<PackageReference Include="xunit.extensibility.core" Version="2.9.2" />
|
<PackageReference Include="xunit.extensibility.core" Version="2.9.2" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\DD.Persistence.Database.Postgres\DD.Persistence.Database.Postgres.csproj" />
|
<ProjectReference Include="..\DD.Persistence.Database.Postgres\DD.Persistence.Database.Postgres.csproj" />
|
||||||
|
<ProjectReference Include="..\DD.Persistence.Repository\DD.Persistence.Repository.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
using DD.Persistence.Database.Model;
|
using DD.Persistence.Database.Model;
|
||||||
using DD.Persistence.Database.Postgres.Repositories;
|
using DD.Persistence.Repository.Repositories;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace DD.Persistence.Repository.Test;
|
namespace DD.Persistence.Repository.Test;
|
||||||
public class SetpointRepositoryShould : IClassFixture<RepositoryTestFixture>
|
public class SetpointRepositoryShould : IClassFixture<RepositoryTestFixture>
|
||||||
@ -24,6 +29,7 @@ public class SetpointRepositoryShould : IClassFixture<RepositoryTestFixture>
|
|||||||
var value = GetJsonFromObject(22);
|
var value = GetJsonFromObject(22);
|
||||||
await sut.Add(id, value, Guid.NewGuid(), CancellationToken.None);
|
await sut.Add(id, value, Guid.NewGuid(), CancellationToken.None);
|
||||||
|
|
||||||
|
var t = fixture.dbContainer.GetConnectionString();
|
||||||
//act
|
//act
|
||||||
var result = await sut.GetCurrent([id], CancellationToken.None);
|
var result = await sut.GetCurrent([id], CancellationToken.None);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Helpers;
|
namespace DD.Persistence.Repository;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Цикличный массив
|
/// Цикличный массив
|
||||||
/// </summary>
|
/// </summary>
|
19
DD.Persistence.Repository/DD.Persistence.Repository.csproj
Normal file
19
DD.Persistence.Repository/DD.Persistence.Repository.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||||
|
<PackageReference Include="UuidExtensions" Version="1.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DD.Persistence.Database\DD.Persistence.Database.csproj" />
|
||||||
|
<ProjectReference Include="..\DD.Persistence\DD.Persistence.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -1,35 +1,44 @@
|
|||||||
using DD.Persistence.Database.Entity;
|
using DD.Persistence.Database.Entity;
|
||||||
using DD.Persistence.Database.Postgres.Repositories;
|
|
||||||
using DD.Persistence.Database.Postgres.RepositoriesCached;
|
|
||||||
using DD.Persistence.Database.Repositories;
|
|
||||||
using DD.Persistence.Database.RepositoriesCached;
|
|
||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.ChangeLog;
|
|
||||||
using DD.Persistence.Repositories;
|
using DD.Persistence.Repositories;
|
||||||
|
using DD.Persistence.Repository.Repositories;
|
||||||
|
using DD.Persistence.Repository.RepositoriesCached;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace DD.Persistence.Database;
|
namespace DD.Persistence.Repository;
|
||||||
|
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
{
|
{
|
||||||
|
public static void MapsterSetup()
|
||||||
|
{
|
||||||
|
TypeAdapterConfig.GlobalSettings.Default.Config
|
||||||
|
.ForType<TechMessageDto, TechMessage>()
|
||||||
|
.Ignore(dest => dest.System, dest => dest.SystemId);
|
||||||
|
|
||||||
|
TypeAdapterConfig<ChangeLog, ChangeLogDto>.NewConfig()
|
||||||
|
.Map(dest => dest.Value, src => new ChangeLogValuesDto()
|
||||||
|
{
|
||||||
|
Value = src.Value,
|
||||||
|
Id = src.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
|
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
|
||||||
typeAdapterConfig.RuleMap.Clear();
|
typeAdapterConfig.RuleMap.Clear();
|
||||||
typeAdapterConfig.Scan(Assembly.GetExecutingAssembly());
|
typeAdapterConfig.Scan(Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
MapsterSetup.Configure();
|
MapsterSetup();
|
||||||
|
|
||||||
services.AddTransient<ISetpointRepository, SetpointRepository>();
|
services.AddTransient<ISetpointRepository, SetpointRepository>();
|
||||||
services.AddTransient<IChangeLogCommitRepository, ChangeLogCommitRepository>();
|
|
||||||
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
|
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
|
||||||
services.AddTransient<ITimestampedValuesRepository, TimestampedValuesRepository>();
|
services.AddTransient<ITimestampedValuesRepository, TimestampedValuesRepository>();
|
||||||
services.AddTransient<ITechMessagesRepository, TechMessagesRepository>();
|
services.AddTransient<ITechMessagesRepository, TechMessagesRepository>();
|
||||||
services.AddTransient<IParameterRepository, ParameterRepository>();
|
services.AddTransient<IParameterRepository, ParameterRepository>();
|
||||||
services.AddTransient<IDataSourceSystemRepository, DataSourceSystemCachedRepository>();
|
services.AddTransient<IDataSourceSystemRepository, DataSourceSystemCachedRepository>();
|
||||||
services.AddTransient<ISchemePropertyRepository, SchemePropertyCachedRepository>();
|
services.AddTransient<IDataSchemeRepository, DataSchemeCachedRepository>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ using System.Collections.Concurrent;
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Extensions;
|
namespace DD.Persistence.Repository.Extensions;
|
||||||
|
|
||||||
public static class EFExtensionsSortBy
|
public static class EFExtensionsSortBy
|
||||||
{
|
{
|
@ -1,16 +1,30 @@
|
|||||||
using DD.Persistence.Database.EntityAbstractions;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using DD.Persistence.Extensions;
|
|
||||||
using DD.Persistence.Models.Common;
|
|
||||||
using DD.Persistence.Models.Requests;
|
using DD.Persistence.Models.Requests;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using DD.Persistence.Models.Common;
|
||||||
|
using DD.Persistence.ModelsAbstractions;
|
||||||
|
using DD.Persistence.Database.EntityAbstractions;
|
||||||
|
using DD.Persistence.Extensions;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Helpers;
|
namespace DD.Persistence.Repository;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// класс с набором методов, необходимых для фильтрации записей
|
/// класс с набором методов, необходимых для фильтрации записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class QueryBuilders
|
public static class QueryBuilders
|
||||||
{
|
{
|
||||||
|
public static IQueryable<TEntity> Apply<TEntity>(this IQueryable<TEntity> query, DateTimeOffset momentUtc)
|
||||||
|
where TEntity : class, IChangeLog
|
||||||
|
{
|
||||||
|
momentUtc = momentUtc.ToUniversalTime();
|
||||||
|
|
||||||
|
query = query
|
||||||
|
.Where(e => e.Creation <= momentUtc)
|
||||||
|
.Where(e => e.Obsolete == null || e.Obsolete >= momentUtc);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static async Task<PaginationContainer<TDto>> ApplyPagination<TEntity, TDto>(
|
public static async Task<PaginationContainer<TDto>> ApplyPagination<TEntity, TDto>(
|
||||||
this IQueryable<TEntity> query,
|
this IQueryable<TEntity> query,
|
||||||
PaginationRequest request,
|
PaginationRequest request,
|
255
DD.Persistence.Repository/Repositories/ChangeLogRepository.cs
Normal file
255
DD.Persistence.Repository/Repositories/ChangeLogRepository.cs
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
using DD.Persistence.Database.Entity;
|
||||||
|
using DD.Persistence.Models;
|
||||||
|
using DD.Persistence.Models.Common;
|
||||||
|
using DD.Persistence.Models.Requests;
|
||||||
|
using DD.Persistence.Repositories;
|
||||||
|
using Mapster;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using UuidExtensions;
|
||||||
|
|
||||||
|
namespace DD.Persistence.Repository.Repositories;
|
||||||
|
public class ChangeLogRepository : IChangeLogRepository
|
||||||
|
{
|
||||||
|
private readonly DbContext db;
|
||||||
|
|
||||||
|
public ChangeLogRepository(DbContext db)
|
||||||
|
{
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
||||||
|
{
|
||||||
|
var entities = new List<ChangeLog>();
|
||||||
|
foreach (var dto in dtos)
|
||||||
|
{
|
||||||
|
var entity = CreateEntityFromDto(idAuthor, idDiscriminator, dto);
|
||||||
|
entities.Add(entity);
|
||||||
|
}
|
||||||
|
db.Set<ChangeLog>().AddRange(entities);
|
||||||
|
|
||||||
|
var result = await db.SaveChangesAsync(token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> MarkAsDeleted(Guid idEditor, IEnumerable<Guid> ids, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>()
|
||||||
|
.Where(s => ids.Contains(s.Id))
|
||||||
|
.Where(s => s.Obsolete == null);
|
||||||
|
|
||||||
|
if (query.Count() != ids.Count())
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Count of active items not equal count of ids", nameof(ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var result = await MarkAsObsolete(idEditor, entities, token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>()
|
||||||
|
.Where(s => s.IdDiscriminator == idDiscriminator)
|
||||||
|
.Where(e => e.Obsolete == null);
|
||||||
|
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var result = await MarkAsObsolete(idEditor, entities, token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> MarkAsObsolete(Guid idEditor, IEnumerable<ChangeLog> entities, CancellationToken token)
|
||||||
|
{
|
||||||
|
var updateTime = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
entity.Obsolete = updateTime;
|
||||||
|
entity.IdEditor = idEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await db.SaveChangesAsync(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = 0;
|
||||||
|
|
||||||
|
using var transaction = await db.Database.BeginTransactionAsync(token);
|
||||||
|
|
||||||
|
result += await MarkAsDeleted(idAuthor, idDiscriminator, token);
|
||||||
|
result += await AddRange(idAuthor, idDiscriminator, dtos, token);
|
||||||
|
|
||||||
|
await transaction.CommitAsync(token);
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> UpdateRange(Guid idEditor, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
||||||
|
{
|
||||||
|
var dbSet = db.Set<ChangeLog>();
|
||||||
|
|
||||||
|
var updatedIds = dtos.Select(d => d.Id);
|
||||||
|
var updatedEntities = dbSet
|
||||||
|
.Where(s => updatedIds.Contains(s.Id))
|
||||||
|
.ToDictionary(s => s.Id);
|
||||||
|
|
||||||
|
var result = 0;
|
||||||
|
using var transaction = await db.Database.BeginTransactionAsync(token);
|
||||||
|
|
||||||
|
foreach (var dto in dtos)
|
||||||
|
{
|
||||||
|
var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id);
|
||||||
|
if (updatedEntity is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
var newEntity = CreateEntityFromDto(idEditor, updatedEntity.IdDiscriminator, dto);
|
||||||
|
dbSet.Add(newEntity);
|
||||||
|
|
||||||
|
updatedEntity.IdNext = newEntity.Id;
|
||||||
|
updatedEntity.Obsolete = DateTimeOffset.UtcNow;
|
||||||
|
updatedEntity.IdEditor = idEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await db.SaveChangesAsync(token);
|
||||||
|
await transaction.CommitAsync(token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PaginationContainer<ChangeLogValuesDto>> GetByDate(
|
||||||
|
Guid idDiscriminator,
|
||||||
|
DateTimeOffset momentUtc,
|
||||||
|
PaginationRequest paginationRequest,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = CreateQuery(idDiscriminator);
|
||||||
|
query = query.Apply(momentUtc);
|
||||||
|
|
||||||
|
var result = await query.ApplyPagination(paginationRequest, Convert, token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<ChangeLog> CreateQuery(Guid idDiscriminator)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>().Where(e => e.IdDiscriminator == idDiscriminator);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>().Where(s => s.IdDiscriminator == idDiscriminator);
|
||||||
|
|
||||||
|
var min = new DateTimeOffset(dateBegin.ToUniversalTime().Date, TimeSpan.Zero);
|
||||||
|
var max = new DateTimeOffset(dateEnd.ToUniversalTime().Date, TimeSpan.Zero);
|
||||||
|
|
||||||
|
var createdQuery = query.Where(e => e.Creation >= min && e.Creation <= max);
|
||||||
|
var editedQuery = query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
|
||||||
|
|
||||||
|
query = createdQuery.Union(editedQuery);
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var dtos = entities.Select(e => e.Adapt<ChangeLogDto>());
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>().Where(e => e.IdDiscriminator == idDiscriminator);
|
||||||
|
|
||||||
|
var datesCreateQuery = query
|
||||||
|
.Select(e => e.Creation)
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
var datesCreate = await datesCreateQuery.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var datesUpdateQuery = query
|
||||||
|
.Where(e => e.Obsolete != null)
|
||||||
|
.Select(e => e.Obsolete!.Value)
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
var datesUpdate = await datesUpdateQuery.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var dates = Enumerable.Concat(datesCreate, datesUpdate);
|
||||||
|
var datesOnly = dates
|
||||||
|
.Select(d => new DateOnly(d.Year, d.Month, d.Day))
|
||||||
|
.Distinct()
|
||||||
|
.OrderBy(d => d);
|
||||||
|
|
||||||
|
return datesOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChangeLog CreateEntityFromDto(Guid idAuthor, Guid idDiscriminator, ChangeLogValuesDto dto)
|
||||||
|
{
|
||||||
|
var entity = new ChangeLog()
|
||||||
|
{
|
||||||
|
Id = Uuid7.Guid(),
|
||||||
|
Creation = DateTimeOffset.UtcNow,
|
||||||
|
IdAuthor = idAuthor,
|
||||||
|
IdDiscriminator = idDiscriminator,
|
||||||
|
IdEditor = idAuthor,
|
||||||
|
|
||||||
|
Value = dto.Value
|
||||||
|
};
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<ChangeLogValuesDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
|
||||||
|
{
|
||||||
|
var date = dateBegin.ToUniversalTime();
|
||||||
|
var query = this.db.Set<ChangeLog>()
|
||||||
|
.Where(e => e.IdDiscriminator == idDiscriminator)
|
||||||
|
.Where(e => e.Creation >= date || e.Obsolete >= date);
|
||||||
|
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var dtos = entities.Select(Convert);
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>()
|
||||||
|
.Where(e => e.IdDiscriminator == idDiscriminator)
|
||||||
|
.GroupBy(e => 1)
|
||||||
|
.Select(group => new
|
||||||
|
{
|
||||||
|
Min = group.Min(e => e.Creation),
|
||||||
|
Max = group.Max(e => (e.Obsolete.HasValue && e.Obsolete > e.Creation)
|
||||||
|
? e.Obsolete.Value
|
||||||
|
: e.Creation),
|
||||||
|
});
|
||||||
|
|
||||||
|
var values = await query.FirstOrDefaultAsync(token);
|
||||||
|
|
||||||
|
if (values is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DatesRangeDto
|
||||||
|
{
|
||||||
|
From = values.Min,
|
||||||
|
To = values.Max,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChangeLogValuesDto Convert(ChangeLog entity) => entity.Adapt<ChangeLogValuesDto>();
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
using DD.Persistence.Database.Entity;
|
||||||
|
using DD.Persistence.Models;
|
||||||
|
using DD.Persistence.Repositories;
|
||||||
|
using Mapster;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DD.Persistence.Repository.Repositories;
|
||||||
|
public class DataSchemeRepository : IDataSchemeRepository
|
||||||
|
{
|
||||||
|
protected DbContext db;
|
||||||
|
public DataSchemeRepository(DbContext db)
|
||||||
|
{
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
protected virtual IQueryable<DataScheme> GetQueryReadOnly() => db.Set<DataScheme>();
|
||||||
|
|
||||||
|
public virtual async Task Add(DataSchemeDto dataSourceSystemDto, CancellationToken token)
|
||||||
|
{
|
||||||
|
var entity = dataSourceSystemDto.Adapt<DataScheme>();
|
||||||
|
|
||||||
|
await db.Set<DataScheme>().AddAsync(entity, token);
|
||||||
|
await db.SaveChangesAsync(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<DataSchemeDto?> Get(Guid dataSchemeId, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = GetQueryReadOnly()
|
||||||
|
.Where(e => e.DiscriminatorId == dataSchemeId);
|
||||||
|
var entity = await query.ToArrayAsync();
|
||||||
|
var dto = entity.Select(e => e.Adapt<DataSchemeDto>()).FirstOrDefault();
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ using DD.Persistence.Repositories;
|
|||||||
using Mapster;
|
using Mapster;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Repositories;
|
namespace DD.Persistence.Repository.Repositories;
|
||||||
public class DataSourceSystemRepository : IDataSourceSystemRepository
|
public class DataSourceSystemRepository : IDataSourceSystemRepository
|
||||||
{
|
{
|
||||||
protected DbContext db;
|
protected DbContext db;
|
||||||
@ -12,7 +12,7 @@ public class DataSourceSystemRepository : IDataSourceSystemRepository
|
|||||||
{
|
{
|
||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
protected IQueryable<DataSourceSystem> GetQueryReadOnly() => db.Set<DataSourceSystem>();
|
protected virtual IQueryable<DataSourceSystem> GetQueryReadOnly() => db.Set<DataSourceSystem>();
|
||||||
|
|
||||||
public virtual async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token)
|
public virtual async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token)
|
||||||
{
|
{
|
@ -5,7 +5,7 @@ using DD.Persistence.Models;
|
|||||||
using DD.Persistence.Repositories;
|
using DD.Persistence.Repositories;
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Repositories;
|
namespace DD.Persistence.Repository.Repositories;
|
||||||
public class ParameterRepository : IParameterRepository
|
public class ParameterRepository : IParameterRepository
|
||||||
{
|
{
|
||||||
private DbContext db;
|
private DbContext db;
|
||||||
@ -15,7 +15,7 @@ public class ParameterRepository : IParameterRepository
|
|||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IQueryable<ParameterData> GetQueryReadOnly() => db.Set<ParameterData>();
|
protected virtual IQueryable<ParameterData> GetQueryReadOnly() => db.Set<ParameterData>();
|
||||||
|
|
||||||
public async Task<DatesRangeDto> GetDatesRangeAsync(Guid idDiscriminator, CancellationToken token)
|
public async Task<DatesRangeDto> GetDatesRangeAsync(Guid idDiscriminator, CancellationToken token)
|
||||||
{
|
{
|
@ -6,7 +6,7 @@ using Mapster;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Repositories
|
namespace DD.Persistence.Repository.Repositories
|
||||||
{
|
{
|
||||||
public class SetpointRepository : ISetpointRepository
|
public class SetpointRepository : ISetpointRepository
|
||||||
{
|
{
|
||||||
@ -16,7 +16,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
|
|||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
|
protected virtual IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
|
||||||
|
|
||||||
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(
|
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(
|
||||||
IEnumerable<Guid> setpointKeys,
|
IEnumerable<Guid> setpointKeys,
|
@ -7,7 +7,7 @@ using DD.Persistence.Repositories;
|
|||||||
using Mapster;
|
using Mapster;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Repositories
|
namespace DD.Persistence.Repository.Repositories
|
||||||
{
|
{
|
||||||
public class TechMessagesRepository : ITechMessagesRepository
|
public class TechMessagesRepository : ITechMessagesRepository
|
||||||
{
|
{
|
||||||
@ -20,7 +20,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
|
|||||||
this.sourceSystemRepository = sourceSystemRepository;
|
this.sourceSystemRepository = sourceSystemRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>()
|
protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>()
|
||||||
.Include(e => e.System);
|
.Include(e => e.System);
|
||||||
|
|
||||||
public async Task<PaginationContainer<TechMessageDto>> GetPage(PaginationRequest request, CancellationToken token)
|
public async Task<PaginationContainer<TechMessageDto>> GetPage(PaginationRequest request, CancellationToken token)
|
@ -1,25 +1,22 @@
|
|||||||
using DD.Persistence.Database.Entity;
|
using DD.Persistence.Database.Entity;
|
||||||
using DD.Persistence.Database.Postgres.Helpers;
|
|
||||||
using DD.Persistence.Filter.Models.Abstractions;
|
|
||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
using DD.Persistence.Repositories;
|
using DD.Persistence.Repositories;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.Repositories;
|
namespace DD.Persistence.Repository.Repositories;
|
||||||
public class TimestampedValuesRepository : ITimestampedValuesRepository
|
public class TimestampedValuesRepository : ITimestampedValuesRepository
|
||||||
{
|
{
|
||||||
private readonly DbContext db;
|
private readonly DbContext db;
|
||||||
private readonly ISchemePropertyRepository schemePropertyRepository;
|
|
||||||
public TimestampedValuesRepository(DbContext db, ISchemePropertyRepository schemePropertyRepository)
|
public TimestampedValuesRepository(DbContext db)
|
||||||
{
|
{
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.schemePropertyRepository = schemePropertyRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IQueryable<TimestampedValues> GetQueryReadOnly() => db.Set<TimestampedValues>();
|
protected virtual IQueryable<TimestampedValues> GetQueryReadOnly() => this.db.Set<TimestampedValues>();
|
||||||
|
|
||||||
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
|
public async virtual Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
|
||||||
{
|
{
|
||||||
var timestampedValuesEntities = dtos.Select(dto => new TimestampedValues()
|
var timestampedValuesEntities = dtos.Select(dto => new TimestampedValues()
|
||||||
{
|
{
|
||||||
@ -28,46 +25,35 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
|||||||
Values = dto.Values.Values.ToArray()
|
Values = dto.Values.Values.ToArray()
|
||||||
});
|
});
|
||||||
|
|
||||||
await db.AddRangeAsync(timestampedValuesEntities, token);
|
await db.Set<TimestampedValues>().AddRangeAsync(timestampedValuesEntities, token);
|
||||||
|
|
||||||
var result = await db.SaveChangesAsync(token);
|
var result = await db.SaveChangesAsync(token);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IDictionary<Guid, IEnumerable<(DateTimeOffset Timestamp, object[] Values)>>> Get(IEnumerable<Guid> discriminatorIds,
|
public async virtual Task<IDictionary<Guid, IEnumerable<(DateTimeOffset Timestamp, object[] Values)>>> Get(IEnumerable<Guid> discriminatorIds,
|
||||||
DateTimeOffset? geTimestamp,
|
DateTimeOffset? timestampBegin,
|
||||||
TNode? filterTree,
|
|
||||||
IEnumerable<string>? columnNames,
|
IEnumerable<string>? columnNames,
|
||||||
int skip,
|
int skip,
|
||||||
int take,
|
int take,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var resultQuery = Array.Empty<TimestampedValues>().AsQueryable();
|
|
||||||
foreach (var discriminatorId in discriminatorIds)
|
|
||||||
{
|
|
||||||
var scheme = await schemePropertyRepository.Get(discriminatorId, token);
|
|
||||||
if (scheme == null)
|
|
||||||
throw new NotSupportedException($"Для переданного дискриминатора {discriminatorId} не была обнаружена схема данных");
|
|
||||||
|
|
||||||
var geTimestampUtc = geTimestamp!.Value.ToUniversalTime();
|
|
||||||
var query = GetQueryReadOnly()
|
var query = GetQueryReadOnly()
|
||||||
.Where(e => e.DiscriminatorId == discriminatorId)
|
.Where(entity => discriminatorIds.Contains(entity.DiscriminatorId));
|
||||||
.Where(entity => entity.Timestamp >= geTimestampUtc);
|
|
||||||
|
|
||||||
if (filterTree != null)
|
// Фильтрация по дате
|
||||||
query = query.ApplyFilter(scheme, filterTree);
|
if (timestampBegin.HasValue)
|
||||||
|
{
|
||||||
resultQuery = resultQuery.Any() ? resultQuery.Union(query) : query;
|
query = ApplyGeTimestamp(query, timestampBegin.Value);
|
||||||
}
|
}
|
||||||
var groupedQuery = resultQuery!
|
|
||||||
.GroupBy(e => e.DiscriminatorId)
|
|
||||||
.Select(g => KeyValuePair.Create(
|
|
||||||
g.Key,
|
|
||||||
g.OrderBy(i => i.Timestamp).Skip(skip).Take(take))
|
|
||||||
);
|
|
||||||
|
|
||||||
var entities = await groupedQuery.ToArrayAsync(token);
|
// Группировка отсортированных значений по DiscriminatorId
|
||||||
|
var groupQuery = query
|
||||||
|
.GroupBy(e => e.DiscriminatorId)
|
||||||
|
.Select(g => KeyValuePair.Create(g.Key, g.OrderBy(i => i.Timestamp).Skip(skip).Take(take)));
|
||||||
|
var entities = await groupQuery.ToArrayAsync(token);
|
||||||
|
|
||||||
var result = entities.ToDictionary(k => k.Key, v => v.Value.Select(e => (
|
var result = entities.ToDictionary(k => k.Key, v => v.Value.Select(e => (
|
||||||
e.Timestamp,
|
e.Timestamp,
|
||||||
e.Values
|
e.Values
|
||||||
@ -76,7 +62,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
|
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
|
||||||
{
|
{
|
||||||
var query = GetQueryReadOnly()
|
var query = GetQueryReadOnly()
|
||||||
.OrderBy(e => e.Timestamp)
|
.OrderBy(e => e.Timestamp)
|
||||||
@ -91,7 +77,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
|
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
|
||||||
{
|
{
|
||||||
var query = GetQueryReadOnly()
|
var query = GetQueryReadOnly()
|
||||||
.OrderByDescending(e => e.Timestamp)
|
.OrderByDescending(e => e.Timestamp)
|
||||||
@ -107,7 +93,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToDo: прореживание должно осуществляться до материализации
|
// ToDo: прореживание должно осуществляться до материализации
|
||||||
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetResampledData(
|
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetResampledData(
|
||||||
Guid discriminatorId,
|
Guid discriminatorId,
|
||||||
DateTimeOffset dateBegin,
|
DateTimeOffset dateBegin,
|
||||||
double intervalSec = 600d,
|
double intervalSec = 600d,
|
||||||
@ -128,11 +114,10 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetGtDate(Guid discriminatorId, DateTimeOffset gtTimestamp, CancellationToken token)
|
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
|
||||||
{
|
{
|
||||||
var gtTimestampUtc = gtTimestamp.ToUniversalTime();
|
|
||||||
var query = GetQueryReadOnly()
|
var query = GetQueryReadOnly()
|
||||||
.Where(entity => entity.Timestamp > gtTimestampUtc);
|
.Where(e => e.Timestamp > timestampBegin);
|
||||||
var entities = await query.ToArrayAsync(token);
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
var result = entities.Select(e => (
|
var result = entities.Select(e => (
|
||||||
@ -143,7 +128,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
|
public async virtual Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
|
||||||
{
|
{
|
||||||
var query = GetQueryReadOnly()
|
var query = GetQueryReadOnly()
|
||||||
.GroupBy(entity => entity.DiscriminatorId)
|
.GroupBy(entity => entity.DiscriminatorId)
|
||||||
@ -168,12 +153,26 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
|
|||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> Count(Guid discriminatorId, CancellationToken token)
|
public virtual Task<int> Count(Guid discriminatorId, CancellationToken token)
|
||||||
{
|
{
|
||||||
var query = GetQueryReadOnly()
|
var dbSet = db.Set<TimestampedValues>();
|
||||||
.Where(e => e.DiscriminatorId == discriminatorId);
|
var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId);
|
||||||
|
|
||||||
var result = await query.CountAsync(token);
|
return query.CountAsync(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Применить фильтр по дате
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="timestampBegin"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private IQueryable<TimestampedValues> ApplyGeTimestamp(IQueryable<TimestampedValues> query, DateTimeOffset timestampBegin)
|
||||||
|
{
|
||||||
|
var geTimestampUtc = timestampBegin.ToUniversalTime();
|
||||||
|
|
||||||
|
var result = query
|
||||||
|
.Where(entity => entity.Timestamp >= geTimestampUtc);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
@ -1,21 +1,21 @@
|
|||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
|
using DD.Persistence.Repository.Repositories;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using DD.Persistence.Database.Repositories;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Database.RepositoriesCached;
|
namespace DD.Persistence.Repository.RepositoriesCached;
|
||||||
public class SchemePropertyCachedRepository : SchemePropertyRepository
|
public class DataSchemeCachedRepository : DataSchemeRepository
|
||||||
{
|
{
|
||||||
private readonly IMemoryCache memoryCache;
|
private readonly IMemoryCache memoryCache;
|
||||||
|
|
||||||
public SchemePropertyCachedRepository(DbContext db, IMemoryCache memoryCache) : base(db)
|
public DataSchemeCachedRepository(DbContext db, IMemoryCache memoryCache) : base(db)
|
||||||
{
|
{
|
||||||
this.memoryCache = memoryCache;
|
this.memoryCache = memoryCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task AddRange(DataSchemeDto dataSourceSystemDto, CancellationToken token)
|
public override async Task Add(DataSchemeDto dataSourceSystemDto, CancellationToken token)
|
||||||
{
|
{
|
||||||
await base.AddRange(dataSourceSystemDto, token);
|
await base.Add(dataSourceSystemDto, token);
|
||||||
|
|
||||||
memoryCache.Set(dataSourceSystemDto.DiscriminatorId, dataSourceSystemDto);
|
memoryCache.Set(dataSourceSystemDto.DiscriminatorId, dataSourceSystemDto);
|
||||||
}
|
}
|
@ -1,10 +1,10 @@
|
|||||||
using DD.Persistence.Database.Entity;
|
using DD.Persistence.Database.Entity;
|
||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Database.Postgres.Repositories;
|
using DD.Persistence.Repository.Repositories;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
namespace DD.Persistence.Database.Postgres.RepositoriesCached;
|
namespace DD.Persistence.Repository.RepositoriesCached;
|
||||||
public class DataSourceSystemCachedRepository : DataSourceSystemRepository
|
public class DataSourceSystemCachedRepository : DataSourceSystemRepository
|
||||||
{
|
{
|
||||||
private static readonly string SystemCacheKey = $"{typeof(DataSourceSystem).FullName}CacheKey";
|
private static readonly string SystemCacheKey = $"{typeof(DataSourceSystem).FullName}CacheKey";
|
@ -0,0 +1,103 @@
|
|||||||
|
//using DD.Persistence.Models;
|
||||||
|
//using DD.Persistence.Models.Common;
|
||||||
|
//using DD.Persistence.Repositories;
|
||||||
|
//using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
//namespace DD.Persistence.Repository.Repositories;
|
||||||
|
|
||||||
|
//public class TimestampedValuesCachedRepository : TimestampedValuesRepository
|
||||||
|
//{
|
||||||
|
// public static TimestampedValuesDto? FirstByDate { get; private set; }
|
||||||
|
// public static CyclicArray<TimestampedValuesDto> LastData { get; } = new CyclicArray<TimestampedValuesDto>(CacheItemsCount);
|
||||||
|
|
||||||
|
// private const int CacheItemsCount = 3600;
|
||||||
|
|
||||||
|
// public TimestampedValuesCachedRepository(DbContext db, IDataSourceSystemRepository<ValuesIdentityDto> relatedDataRepository) : base(db, relatedDataRepository)
|
||||||
|
// {
|
||||||
|
// //Task.Run(async () =>
|
||||||
|
// //{
|
||||||
|
// // var firstDateItem = await base.GetFirst(CancellationToken.None);
|
||||||
|
// // if (firstDateItem == null)
|
||||||
|
// // {
|
||||||
|
// // return;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // FirstByDate = firstDateItem;
|
||||||
|
|
||||||
|
// // var dtos = await base.GetLast(CacheItemsCount, CancellationToken.None);
|
||||||
|
// // dtos = dtos.OrderBy(d => d.Timestamp);
|
||||||
|
// // LastData.AddRange(dtos);
|
||||||
|
// //}).Wait();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// if (LastData.Count == 0 || LastData[0].Timestamp > dateBegin)
|
||||||
|
// {
|
||||||
|
// var dtos = await base.GetGtDate(discriminatorId, dateBegin, token);
|
||||||
|
// return dtos;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var items = LastData
|
||||||
|
// .Where(i => i.Timestamp >= dateBegin);
|
||||||
|
|
||||||
|
// return items;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
|
||||||
|
// {
|
||||||
|
// var result = await base.AddRange(discriminatorId, dtos, token);
|
||||||
|
// if (result > 0)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// dtos = dtos.OrderBy(x => x.Timestamp);
|
||||||
|
|
||||||
|
// FirstByDate = dtos.First();
|
||||||
|
// LastData.AddRange(dtos);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
|
||||||
|
// {
|
||||||
|
// if (FirstByDate == null)
|
||||||
|
// return null;
|
||||||
|
|
||||||
|
// return await Task.Run(() =>
|
||||||
|
// {
|
||||||
|
// return new DatesRangeDto
|
||||||
|
// {
|
||||||
|
// From = FirstByDate.Timestamp,
|
||||||
|
// To = LastData[^1].Timestamp
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override async Task<IEnumerable<TimestampedValuesDto>> GetResampledData(
|
||||||
|
// Guid discriminatorId,
|
||||||
|
// DateTimeOffset dateBegin,
|
||||||
|
// double intervalSec = 600d,
|
||||||
|
// int approxPointsCount = 1024,
|
||||||
|
// CancellationToken token = default)
|
||||||
|
// {
|
||||||
|
// var dtos = LastData.Where(i => i.Timestamp >= dateBegin);
|
||||||
|
// if (LastData.Count == 0 || LastData[0].Timestamp > dateBegin)
|
||||||
|
// {
|
||||||
|
// dtos = await base.GetGtDate(discriminatorId, dateBegin, token);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var dateEnd = dateBegin.AddSeconds(intervalSec);
|
||||||
|
// dtos = dtos
|
||||||
|
// .Where(i => i.Timestamp <= dateEnd);
|
||||||
|
|
||||||
|
// var ratio = dtos.Count() / approxPointsCount;
|
||||||
|
// if (ratio > 1)
|
||||||
|
// dtos = dtos
|
||||||
|
// .Where((_, index) => index % ratio == 0);
|
||||||
|
|
||||||
|
// return dtos;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
@ -1,305 +0,0 @@
|
|||||||
using DD.Persistence.Database.Repositories;
|
|
||||||
using DD.Persistence.Models.ChangeLog;
|
|
||||||
using DD.Persistence.Models.Common;
|
|
||||||
using DD.Persistence.Models.Requests;
|
|
||||||
using DD.Persistence.Repositories;
|
|
||||||
using DD.Persistence.Services;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using NSubstitute;
|
|
||||||
using UuidExtensions;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Test;
|
|
||||||
public class ChangeLogTest
|
|
||||||
{
|
|
||||||
private readonly IChangeLogCommitRepository changeLogCommitRepository = Substitute.For<IChangeLogCommitRepository>();
|
|
||||||
private readonly IChangeLogRepository changeLogRepository = Substitute.For<IChangeLogRepository>();
|
|
||||||
private ChangeLogService service;
|
|
||||||
|
|
||||||
public ChangeLogTest()
|
|
||||||
{
|
|
||||||
var memoryCache = new MemoryCache(new MemoryCacheOptions());
|
|
||||||
service = new ChangeLogService(memoryCache, changeLogCommitRepository, changeLogRepository);
|
|
||||||
}
|
|
||||||
[Fact]
|
|
||||||
public async Task AddRange()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Uuid7.Guid();
|
|
||||||
var commitRequest = new ChangeLogCommitCreateRequest(discriminatorId, Uuid7.Guid(), "Добавление нескольких значений");
|
|
||||||
var commit = new ChangeLogCommitDto() {
|
|
||||||
IdAuthor = commitRequest.IdAuthor,
|
|
||||||
Comment = commitRequest.Comment
|
|
||||||
|
|
||||||
};
|
|
||||||
commit.Id = Uuid7.Guid();
|
|
||||||
var dtos = GenerateChangeLogRequests(2);
|
|
||||||
|
|
||||||
changeLogCommitRepository.Add(Arg.Any<ChangeLogCommitCreateRequest>(), Arg.Any<CancellationToken>()).Returns(commit);
|
|
||||||
changeLogRepository
|
|
||||||
.AddRange(
|
|
||||||
Arg.Any<ChangeLogCommitDto>(),
|
|
||||||
Arg.Any<IEnumerable<IDictionary<string, object>>>(),
|
|
||||||
Arg.Any<CancellationToken>())
|
|
||||||
.Returns(2);
|
|
||||||
|
|
||||||
//act
|
|
||||||
var addRangeResult = await service
|
|
||||||
.AddRange(commitRequest, dtos, CancellationToken.None);
|
|
||||||
addRangeResult = await service
|
|
||||||
.AddRange(commitRequest, dtos, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
await changeLogCommitRepository.Received(1).Add(commitRequest, CancellationToken.None);
|
|
||||||
await changeLogRepository.Received(2).AddRange(Arg.Any<ChangeLogCommitDto>(), dtos, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task UpdateRange()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Uuid7.Guid();
|
|
||||||
var commitRequest = new ChangeLogCommitCreateRequest(discriminatorId, Uuid7.Guid(), "Изменение нескольких значений");
|
|
||||||
var commit = new ChangeLogCommitDto() {
|
|
||||||
IdAuthor = commitRequest.IdAuthor,
|
|
||||||
Comment = commitRequest.Comment
|
|
||||||
};
|
|
||||||
commit.Id = Uuid7.Guid();
|
|
||||||
|
|
||||||
var dtos = GenerateChangeLogValuesDto(2);
|
|
||||||
|
|
||||||
changeLogCommitRepository.Add(Arg.Any<ChangeLogCommitCreateRequest>(), Arg.Any<CancellationToken>()).Returns(commit);
|
|
||||||
|
|
||||||
changeLogRepository
|
|
||||||
.UpdateRange(
|
|
||||||
Arg.Any<ChangeLogCommitDto>(),
|
|
||||||
Arg.Any<IEnumerable<ChangeLogBaseDto>>(),
|
|
||||||
Arg.Any<CancellationToken>())
|
|
||||||
.Returns(2);
|
|
||||||
|
|
||||||
//act
|
|
||||||
var updateRangeResult = await service
|
|
||||||
.UpdateRange(commitRequest, dtos, CancellationToken.None);
|
|
||||||
|
|
||||||
updateRangeResult = await service
|
|
||||||
.UpdateRange(commitRequest, dtos, CancellationToken.None);
|
|
||||||
|
|
||||||
updateRangeResult = await service
|
|
||||||
.UpdateRange(commitRequest, dtos, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
await changeLogCommitRepository.Received(1).Add(commitRequest, CancellationToken.None);
|
|
||||||
await changeLogRepository.Received(3).UpdateRange(Arg.Any<ChangeLogCommitDto>(), dtos, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MarkAsDeleted()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Uuid7.Guid();
|
|
||||||
var commitRequest = new ChangeLogCommitCreateRequest(discriminatorId, Uuid7.Guid(), "Удаление нескольких значений");
|
|
||||||
var commit = new ChangeLogCommitDto()
|
|
||||||
{
|
|
||||||
IdAuthor = commitRequest.IdAuthor,
|
|
||||||
Comment = commitRequest.Comment
|
|
||||||
};
|
|
||||||
commit.Id = Uuid7.Guid();
|
|
||||||
var dtos = GenerateChangeLogValuesDto(2);
|
|
||||||
var dtoIds = dtos.Select(d => d.Id);
|
|
||||||
|
|
||||||
changeLogCommitRepository.Add(Arg.Any<ChangeLogCommitCreateRequest>(), Arg.Any<CancellationToken>()).Returns(commit);
|
|
||||||
changeLogRepository
|
|
||||||
.MarkAsDeleted(
|
|
||||||
Arg.Any<IEnumerable<Guid>>(),
|
|
||||||
Arg.Any<ChangeLogCommitDto>(),
|
|
||||||
Arg.Any<CancellationToken>())
|
|
||||||
.Returns(2);
|
|
||||||
|
|
||||||
//act
|
|
||||||
var markAsDeletedResult = await service
|
|
||||||
.MarkAsDeleted(dtoIds, commitRequest, CancellationToken.None);
|
|
||||||
markAsDeletedResult = await service
|
|
||||||
.MarkAsDeleted(dtoIds, commitRequest, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
await changeLogCommitRepository.Received(1).Add(commitRequest, CancellationToken.None);
|
|
||||||
await changeLogRepository.Received(2).MarkAsDeleted(dtoIds, commit, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ClearAndAddRange()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Uuid7.Guid();
|
|
||||||
var commitRequest = new ChangeLogCommitCreateRequest(discriminatorId, Uuid7.Guid(), "Удаление и добавление нескольких значений");
|
|
||||||
var commit = new ChangeLogCommitDto() {
|
|
||||||
IdAuthor = commitRequest.IdAuthor,
|
|
||||||
Comment = commitRequest.Comment
|
|
||||||
};
|
|
||||||
commit.Id = Uuid7.Guid();
|
|
||||||
var dtos = GenerateChangeLogRequests(2);
|
|
||||||
|
|
||||||
changeLogCommitRepository.Add(Arg.Any<ChangeLogCommitCreateRequest>(), Arg.Any<CancellationToken>()).Returns(commit);
|
|
||||||
changeLogRepository
|
|
||||||
.ClearAndAddRange(
|
|
||||||
Arg.Any<ChangeLogCommitDto>(),
|
|
||||||
Arg.Any<IEnumerable<IDictionary<string, object>>>(),
|
|
||||||
Arg.Any<CancellationToken>())
|
|
||||||
.Returns(2);
|
|
||||||
|
|
||||||
//act
|
|
||||||
var clearAndAddResult = await service
|
|
||||||
.ClearAndAddRange(commitRequest, dtos, CancellationToken.None);
|
|
||||||
clearAndAddResult = await service
|
|
||||||
.ClearAndAddRange(commitRequest, dtos, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
await changeLogCommitRepository.Received(1).Add(commitRequest, CancellationToken.None);
|
|
||||||
await changeLogRepository.Received(2).ClearAndAddRange(Arg.Any<ChangeLogCommitDto>(), dtos, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetByDate()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Uuid7.Guid();
|
|
||||||
var paginationRequest = new PaginationRequest()
|
|
||||||
{
|
|
||||||
Skip = 0,
|
|
||||||
Take = 1000
|
|
||||||
};
|
|
||||||
var dtos = GenerateChangeLogValuesDto(5);
|
|
||||||
var items = new PaginationContainer<ChangeLogBaseDto>()
|
|
||||||
{
|
|
||||||
Take = paginationRequest.Take,
|
|
||||||
Skip = paginationRequest.Skip,
|
|
||||||
Items = dtos,
|
|
||||||
Count = 10
|
|
||||||
};
|
|
||||||
var momentDate = DateTime.UtcNow;
|
|
||||||
|
|
||||||
changeLogRepository
|
|
||||||
.GetByDate(
|
|
||||||
Arg.Any<Guid>(),
|
|
||||||
Arg.Any<DateTimeOffset>(),
|
|
||||||
Arg.Any<PaginationRequest>(),
|
|
||||||
Arg.Any<CancellationToken>())
|
|
||||||
.Returns(items);
|
|
||||||
|
|
||||||
//act
|
|
||||||
var actualItems = await service
|
|
||||||
.GetByDate(discriminatorId, momentDate, paginationRequest, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
await changeLogRepository.Received(1).GetByDate(discriminatorId, momentDate, paginationRequest, CancellationToken.None);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetChangeLogForInterval()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Uuid7.Guid();
|
|
||||||
var dtos = GenerateChangeLogDto(5);
|
|
||||||
|
|
||||||
var dateBegin = DateTimeOffset.UtcNow.AddDays(-5);
|
|
||||||
var dateEnd = DateTimeOffset.UtcNow;
|
|
||||||
|
|
||||||
changeLogCommitRepository
|
|
||||||
.GetChangeLogForInterval(
|
|
||||||
Arg.Any<Guid>(),
|
|
||||||
Arg.Any<DateTimeOffset>(),
|
|
||||||
Arg.Any<DateTimeOffset>(),
|
|
||||||
Arg.Any<CancellationToken>())
|
|
||||||
.Returns(dtos);
|
|
||||||
|
|
||||||
//act
|
|
||||||
var actualItems = await service
|
|
||||||
.GetChangeLogForInterval(discriminatorId, dateBegin, dateEnd, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
await changeLogCommitRepository.Received(1).GetChangeLogForInterval(discriminatorId, dateBegin, dateEnd, CancellationToken.None);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetDatesChange()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Uuid7.Guid();
|
|
||||||
|
|
||||||
var dateBegin = DateTimeOffset.UtcNow.AddDays(-5);
|
|
||||||
var dateEnd = DateTimeOffset.UtcNow;
|
|
||||||
|
|
||||||
var dateOnlyBegin = new DateOnly(dateBegin.Year, dateBegin.Month, dateBegin.Day);
|
|
||||||
var dateOnlyEnd = new DateOnly(dateEnd.Year, dateEnd.Month, dateEnd.Day);
|
|
||||||
|
|
||||||
var dtos = new List<DateOnly>() { dateOnlyBegin, dateOnlyEnd };
|
|
||||||
|
|
||||||
changeLogCommitRepository
|
|
||||||
.GetDatesChange(
|
|
||||||
Arg.Any<Guid>(),
|
|
||||||
Arg.Any<CancellationToken>())
|
|
||||||
.Returns(dtos);
|
|
||||||
|
|
||||||
//act
|
|
||||||
var actualItems = await service
|
|
||||||
.GetDatesChange(discriminatorId, CancellationToken.None);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
await changeLogCommitRepository.Received(1).GetDatesChange(discriminatorId, CancellationToken.None);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private IEnumerable<ChangeLogBaseDto> GenerateChangeLogValuesDto(int count)
|
|
||||||
{
|
|
||||||
var items = new List<ChangeLogBaseDto>();
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
items.Add(new ChangeLogBaseDto()
|
|
||||||
{
|
|
||||||
|
|
||||||
Id = Uuid7.Guid(),
|
|
||||||
Value = new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "1", 1 },
|
|
||||||
{ "2", 2 }
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<IDictionary<string, object>> GenerateChangeLogRequests(int count)
|
|
||||||
{
|
|
||||||
var items = new List<IDictionary<string, object>>();
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
items.Add(new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "1", 1 },
|
|
||||||
{ "2", 2 }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<ChangeLogDto> GenerateChangeLogDto(int count)
|
|
||||||
{
|
|
||||||
var items = new List<ChangeLogDto>();
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
items.Add(new ChangeLogDto()
|
|
||||||
{
|
|
||||||
Id = Uuid7.Guid(),
|
|
||||||
Value = new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "1", 1 },
|
|
||||||
{ "2", 2 }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
@ -16,9 +16,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\DD.Persistence.API\DD.Persistence.API.csproj" />
|
<ProjectReference Include="..\DD.Persistence.Database\DD.Persistence.Database.csproj" />
|
||||||
<ProjectReference Include="..\DD.Persistence.Client\DD.Persistence.Client.csproj" />
|
<ProjectReference Include="..\DD.Persistence\DD.Persistence.csproj" />
|
||||||
<ProjectReference Include="..\DD.Persistence.Database.Postgres\DD.Persistence.Database.Postgres.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,278 +0,0 @@
|
|||||||
using Ardalis.Specification.EntityFrameworkCore;
|
|
||||||
using DD.Persistence.Database.Entity;
|
|
||||||
using DD.Persistence.Filter.Models;
|
|
||||||
using DD.Persistence.Filter.Models.Enumerations;
|
|
||||||
using DD.Persistence.Models;
|
|
||||||
using DD.Persistence.Database.Postgres.Helpers;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Test;
|
|
||||||
|
|
||||||
/// ToDo: переписать под Theory
|
|
||||||
public class FilterBuilderShould
|
|
||||||
{
|
|
||||||
private readonly SpecificationEvaluator SpecificationEvaluator;
|
|
||||||
public FilterBuilderShould()
|
|
||||||
{
|
|
||||||
this.SpecificationEvaluator = new SpecificationEvaluator();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestFilterBuilding()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Guid.NewGuid();
|
|
||||||
var dataSchemeProperties = new SchemePropertyDto[]
|
|
||||||
{
|
|
||||||
new SchemePropertyDto()
|
|
||||||
{
|
|
||||||
Index = 0,
|
|
||||||
PropertyName = "A",
|
|
||||||
PropertyKind = JsonValueKind.String
|
|
||||||
},
|
|
||||||
new SchemePropertyDto()
|
|
||||||
{
|
|
||||||
Index = 1,
|
|
||||||
PropertyName = "B",
|
|
||||||
PropertyKind = JsonValueKind.Number
|
|
||||||
},
|
|
||||||
new SchemePropertyDto()
|
|
||||||
{
|
|
||||||
Index = 2,
|
|
||||||
PropertyName = "C",
|
|
||||||
PropertyKind = JsonValueKind.String
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var dataScheme = new DataSchemeDto(discriminatorId, dataSchemeProperties);
|
|
||||||
var filterDate = DateTime.Now.AddMinutes(-1);
|
|
||||||
var root = new TVertex(
|
|
||||||
OperationEnum.Or,
|
|
||||||
new TVertex(
|
|
||||||
OperationEnum.And,
|
|
||||||
new TLeaf(OperationEnum.Greate, "A", filterDate),
|
|
||||||
new TLeaf(OperationEnum.Less, "B", 2.22)
|
|
||||||
),
|
|
||||||
new TLeaf(OperationEnum.Equal, "C", "IsEqualText")
|
|
||||||
);
|
|
||||||
var queryableData = new[]
|
|
||||||
{
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-1),
|
|
||||||
Values = new object[] { filterDate.AddMinutes(-1), 200, "IsEqualText" } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-2),
|
|
||||||
Values = new object[] { filterDate.AddMinutes(1), 2.21, "IsNotEqualText" } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-3),
|
|
||||||
Values = new object[] { filterDate.AddMinutes(-1), 2.22, "IsNotEqualText" } // false
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-4),
|
|
||||||
Values = new object[] { filterDate.AddMinutes(-1), 2.21, "IsNotEqualText" } // false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.AsQueryable();
|
|
||||||
|
|
||||||
//act
|
|
||||||
queryableData = queryableData.ApplyFilter(dataScheme, root);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
var result = queryableData.ToList();
|
|
||||||
|
|
||||||
Assert.NotNull(result);
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
|
|
||||||
var expectedCount = 2;
|
|
||||||
var actualCount = result.Count();
|
|
||||||
Assert.Equal(expectedCount, actualCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestFilterOperations()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Guid.NewGuid();
|
|
||||||
var dataSchemeProperties = new SchemePropertyDto[]
|
|
||||||
{
|
|
||||||
new SchemePropertyDto()
|
|
||||||
{
|
|
||||||
Index = 0,
|
|
||||||
PropertyName = "A",
|
|
||||||
PropertyKind = JsonValueKind.Number
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var dataScheme = new DataSchemeDto(discriminatorId, dataSchemeProperties);
|
|
||||||
var root = new TVertex(
|
|
||||||
OperationEnum.Or,
|
|
||||||
new TVertex(
|
|
||||||
OperationEnum.And,
|
|
||||||
new TVertex(
|
|
||||||
OperationEnum.And,
|
|
||||||
new TVertex(
|
|
||||||
OperationEnum.And,
|
|
||||||
new TVertex(
|
|
||||||
OperationEnum.And,
|
|
||||||
new TLeaf(OperationEnum.Less, "A", 2),
|
|
||||||
new TLeaf(OperationEnum.LessOrEqual, "A", 1.99)
|
|
||||||
),
|
|
||||||
new TLeaf(OperationEnum.GreateOrEqual, "A", 1.97)
|
|
||||||
),
|
|
||||||
new TLeaf(OperationEnum.Greate, "A", 1.96)
|
|
||||||
),
|
|
||||||
new TLeaf(OperationEnum.NotEqual, "A", 1.98)
|
|
||||||
),
|
|
||||||
new TLeaf(OperationEnum.Equal, "A", 1)
|
|
||||||
);
|
|
||||||
var queryableData = new[]
|
|
||||||
{
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-1),
|
|
||||||
Values = new object[] { 1 } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-2),
|
|
||||||
Values = new object[] { 1.96 } // false
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-3),
|
|
||||||
Values = new object[] { 1.97 } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-4),
|
|
||||||
Values = new object[] { 1.98 } // false
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-5),
|
|
||||||
Values = new object[] { 1.99 } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-6),
|
|
||||||
Values = new object[] { 2 } // false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.AsQueryable();
|
|
||||||
|
|
||||||
//act
|
|
||||||
queryableData = queryableData.ApplyFilter(dataScheme, root);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
var result = queryableData.ToList();
|
|
||||||
|
|
||||||
Assert.NotNull(result);
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
|
|
||||||
Assert.NotNull(result);
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
|
|
||||||
var expectedCount = 3;
|
|
||||||
var actualCount = result.Count();
|
|
||||||
Assert.Equal(expectedCount, actualCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestFilterValues()
|
|
||||||
{
|
|
||||||
//arrange
|
|
||||||
var discriminatorId = Guid.NewGuid();
|
|
||||||
var filterDate = DateTimeOffset.Now;
|
|
||||||
var dataSchemeProperties = new SchemePropertyDto[]
|
|
||||||
{
|
|
||||||
new SchemePropertyDto()
|
|
||||||
{
|
|
||||||
Index = 0,
|
|
||||||
PropertyName = "A",
|
|
||||||
PropertyKind = JsonValueKind.Number
|
|
||||||
},
|
|
||||||
new SchemePropertyDto()
|
|
||||||
{
|
|
||||||
Index = 1,
|
|
||||||
PropertyName = "B",
|
|
||||||
PropertyKind = JsonValueKind.Number
|
|
||||||
},
|
|
||||||
new SchemePropertyDto()
|
|
||||||
{
|
|
||||||
Index = 2,
|
|
||||||
PropertyName = "C",
|
|
||||||
PropertyKind = JsonValueKind.String
|
|
||||||
},
|
|
||||||
new SchemePropertyDto()
|
|
||||||
{
|
|
||||||
Index = 3,
|
|
||||||
PropertyName = "D",
|
|
||||||
PropertyKind = JsonValueKind.String
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var dataScheme = new DataSchemeDto(discriminatorId, dataSchemeProperties);
|
|
||||||
|
|
||||||
var root = new TVertex(
|
|
||||||
OperationEnum.Or,
|
|
||||||
new TVertex(
|
|
||||||
OperationEnum.Or,
|
|
||||||
new TVertex(
|
|
||||||
OperationEnum.Or,
|
|
||||||
new TLeaf(OperationEnum.Equal, "A", 1),
|
|
||||||
new TLeaf(OperationEnum.Equal, "B", 1.11)
|
|
||||||
),
|
|
||||||
new TLeaf(OperationEnum.Equal, "C", "IsEqualText")
|
|
||||||
),
|
|
||||||
new TLeaf(OperationEnum.Equal, "D", filterDate)
|
|
||||||
);
|
|
||||||
var queryableData = new[]
|
|
||||||
{
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-1),
|
|
||||||
Values = new object[] { 1, 2.22, "IsNotEqualText", DateTimeOffset.Now.AddMinutes(-1) } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-2),
|
|
||||||
Values = new object[] { 2, 1.11, "IsNotEqualText", DateTimeOffset.Now.AddMinutes(-1) } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-3),
|
|
||||||
Values = new object[] { 2, 2.22, "IsEqualText", DateTimeOffset.Now.AddMinutes(-1) } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-4),
|
|
||||||
Values = new object[] { 2, 2.22, "IsNotEqualText", filterDate } // true
|
|
||||||
},
|
|
||||||
new TimestampedValues {
|
|
||||||
DiscriminatorId = discriminatorId,
|
|
||||||
Timestamp = DateTimeOffset.Now.AddMinutes(-1),
|
|
||||||
Values = new object[] { 2, 2.22, "IsNotEqualText", DateTimeOffset.Now.AddMinutes(-1) } // false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.AsQueryable();
|
|
||||||
|
|
||||||
//act
|
|
||||||
queryableData = queryableData.ApplyFilter(dataScheme, root);
|
|
||||||
|
|
||||||
//assert
|
|
||||||
var result = queryableData.ToList();
|
|
||||||
|
|
||||||
Assert.NotNull(result);
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
|
|
||||||
Assert.NotNull(result);
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
|
|
||||||
var expectedCount = 4;
|
|
||||||
var actualCount = result.Count();
|
|
||||||
Assert.Equal(expectedCount, actualCount);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
using DD.Persistence.Client.Clients.Interfaces;
|
|
||||||
using DD.Persistence.Client.Clients.Mapping;
|
|
||||||
using DD.Persistence.Client.Clients.Mapping.Clients;
|
|
||||||
using DD.Persistence.Models;
|
|
||||||
using DD.Persistence.Models.Configurations;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using NSubstitute;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Test;
|
|
||||||
|
|
||||||
public record FirstTestDto(Guid DiscriminatorId, DateTimeOffset Timestamp, int Id, string? Value);
|
|
||||||
|
|
||||||
public record SecondTestDto(Guid DiscriminatorId, DateTimeOffset Timestamp, int Id, double Capacity);
|
|
||||||
|
|
||||||
public class MappingClientsTest
|
|
||||||
{
|
|
||||||
private readonly ITimestampedValuesClient timestampedValuesClient = Substitute.For<ITimestampedValuesClient>();
|
|
||||||
private readonly ILogger<TimestampedSetMapper> logger = Substitute.For<ILogger<TimestampedSetMapper>>();
|
|
||||||
private readonly TimestampedMappingClient timestampedMappingClient;
|
|
||||||
|
|
||||||
private readonly MappingConfig mappingConfigs;
|
|
||||||
|
|
||||||
|
|
||||||
public MappingClientsTest()
|
|
||||||
{
|
|
||||||
mappingConfigs = GetConfig();
|
|
||||||
var storage = new MapperStorage(mappingConfigs, logger);
|
|
||||||
|
|
||||||
timestampedMappingClient = new TimestampedMappingClient(timestampedValuesClient, storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetMultiMapped()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var discriminatorIds = mappingConfigs.Keys;
|
|
||||||
var firstDiscriminatorId = discriminatorIds.First();
|
|
||||||
var secondDiscriminatorId = discriminatorIds.Last();
|
|
||||||
var getResult = new[]
|
|
||||||
{
|
|
||||||
new TimestampedValuesDto()
|
|
||||||
{
|
|
||||||
DiscriminatorId = firstDiscriminatorId,
|
|
||||||
Timestamp = DateTime.UtcNow,
|
|
||||||
Values = new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ nameof(FirstTestDto.Id), JsonDocument.Parse(JsonSerializer.Serialize(1)).RootElement },
|
|
||||||
{ nameof(FirstTestDto.Value), JsonDocument.Parse(JsonSerializer.Serialize("string1")).RootElement}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TimestampedValuesDto()
|
|
||||||
{
|
|
||||||
DiscriminatorId = secondDiscriminatorId,
|
|
||||||
Timestamp = DateTime.UtcNow,
|
|
||||||
Values = new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ nameof(SecondTestDto.Id), JsonDocument.Parse(JsonSerializer.Serialize(1)).RootElement },
|
|
||||||
{ nameof(SecondTestDto.Capacity), JsonDocument.Parse(JsonSerializer.Serialize(0.1)).RootElement}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
timestampedValuesClient
|
|
||||||
.Get(discriminatorIds, null, null, null, 0, 1, CancellationToken.None)
|
|
||||||
.ReturnsForAnyArgs(getResult);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await timestampedMappingClient.GetMultiMapped(discriminatorIds, null, null, null, 0, 1, CancellationToken.None);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(result);
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
Assert.Equal(getResult.Count(), result.Count());
|
|
||||||
|
|
||||||
var firstActualDto = (FirstTestDto) result[firstDiscriminatorId].First();
|
|
||||||
Assert.NotNull(firstActualDto);
|
|
||||||
|
|
||||||
var actualId = firstActualDto.Id.ToString();
|
|
||||||
var expectedId = getResult[0].Values[nameof(FirstTestDto.Id)].ToString();
|
|
||||||
Assert.Equal(expectedId, actualId);
|
|
||||||
|
|
||||||
var secondActualDto = (SecondTestDto) result[secondDiscriminatorId].First();
|
|
||||||
Assert.NotNull(secondActualDto);
|
|
||||||
|
|
||||||
actualId = secondActualDto.Id.ToString();
|
|
||||||
expectedId = getResult[1].Values[nameof(SecondTestDto.Id)].ToString();
|
|
||||||
Assert.Equal(expectedId, actualId);
|
|
||||||
}
|
|
||||||
private MappingConfig GetConfig()
|
|
||||||
{
|
|
||||||
var config = new MappingConfig();
|
|
||||||
config[Guid.NewGuid()] = typeof(FirstTestDto);
|
|
||||||
config[Guid.NewGuid()] = typeof(SecondTestDto);
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +1,14 @@
|
|||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Repositories;
|
using DD.Persistence.Repositories;
|
||||||
using DD.Persistence.Services;
|
using DD.Persistence.Services;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace DD.Persistence.Test;
|
namespace DD.Persistence.Repository.Test;
|
||||||
public class TimestampedValuesServiceShould
|
public class TimestampedValuesServiceShould
|
||||||
{
|
{
|
||||||
private readonly ITimestampedValuesRepository timestampedValuesRepository = Substitute.For<ITimestampedValuesRepository>();
|
private readonly ITimestampedValuesRepository timestampedValuesRepository = Substitute.For<ITimestampedValuesRepository>();
|
||||||
private readonly ISchemePropertyRepository dataSchemeRepository = Substitute.For<ISchemePropertyRepository>();
|
private readonly IDataSchemeRepository dataSchemeRepository = Substitute.For<IDataSchemeRepository>();
|
||||||
private readonly TimestampedValuesService timestampedValuesService;
|
private TimestampedValuesService timestampedValuesService;
|
||||||
|
|
||||||
public TimestampedValuesServiceShould()
|
public TimestampedValuesServiceShould()
|
||||||
{
|
{
|
||||||
@ -34,21 +33,22 @@ public class TimestampedValuesServiceShould
|
|||||||
.AddHours(-1)
|
.AddHours(-1)
|
||||||
.ToUniversalTime();
|
.ToUniversalTime();
|
||||||
var getResult = await timestampedValuesService
|
var getResult = await timestampedValuesService
|
||||||
.Get(discriminatorIds, geTimestamp, null, columnNames, 0, count, CancellationToken.None);
|
.Get(discriminatorIds, geTimestamp, columnNames, 0, count, CancellationToken.None);
|
||||||
Assert.NotNull(getResult);
|
Assert.NotNull(getResult);
|
||||||
Assert.Empty(getResult);
|
Assert.Empty(getResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<TimestampedValuesDto> Generate(int countToCreate, DateTimeOffset from)
|
private static IEnumerable<TimestampedValuesDto> Generate(int countToCreate, DateTimeOffset from)
|
||||||
{
|
{
|
||||||
|
var result = new List<TimestampedValuesDto>();
|
||||||
for (int i = 0; i < countToCreate; i++)
|
for (int i = 0; i < countToCreate; i++)
|
||||||
{
|
{
|
||||||
var values = new Dictionary<string, object>()
|
var values = new Dictionary<string, object>()
|
||||||
{
|
{
|
||||||
{ "A", GetJsonFromObject(i) },
|
{ "A", i },
|
||||||
{ "B", GetJsonFromObject(i * 1.1) },
|
{ "B", i * 1.1 },
|
||||||
{ "C", GetJsonFromObject($"Any{i}") },
|
{ "C", $"Any{i}" },
|
||||||
{ "D", GetJsonFromObject(DateTimeOffset.Now) }
|
{ "D", DateTimeOffset.Now },
|
||||||
};
|
};
|
||||||
|
|
||||||
yield return new TimestampedValuesDto()
|
yield return new TimestampedValuesDto()
|
||||||
@ -58,11 +58,4 @@ public class TimestampedValuesServiceShould
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsonElement GetJsonFromObject(object value)
|
|
||||||
{
|
|
||||||
var jsonString = JsonSerializer.Serialize(value);
|
|
||||||
var doc = JsonDocument.Parse(jsonString);
|
|
||||||
return doc.RootElement;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user