Project cleanup

This commit is contained in:
Оля Бизюкова 2024-12-10 10:43:12 +05:00
parent 48dd53fce4
commit a6e611ec0d
27 changed files with 617 additions and 633 deletions

View File

@ -12,7 +12,7 @@ namespace Persistence.API.Controllers;
[Route("api/[controller]")] [Route("api/[controller]")]
public class ChangeLogController : ControllerBase, IChangeLogApi public class ChangeLogController : ControllerBase, IChangeLogApi
{ {
private IChangeLogRepository repository; private readonly IChangeLogRepository repository;
public ChangeLogController(IChangeLogRepository repository) public ChangeLogController(IChangeLogRepository repository)
{ {

View File

@ -1,9 +1,9 @@
using System.Net; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Persistence.Models; using Persistence.Models;
using Persistence.Models.Requests; using Persistence.Models.Requests;
using Persistence.Repositories; using Persistence.Repositories;
using System.Net;
namespace Persistence.API.Controllers; namespace Persistence.API.Controllers;
@ -15,115 +15,115 @@ namespace Persistence.API.Controllers;
[Route("api/[controller]")] [Route("api/[controller]")]
public class TechMessagesController : ControllerBase public class TechMessagesController : ControllerBase
{ {
private readonly ITechMessagesRepository techMessagesRepository; private readonly ITechMessagesRepository techMessagesRepository;
private static readonly Dictionary<int, string> categories = new Dictionary<int, string>() private static readonly Dictionary<int, string> categories = new()
{ {
{ 0, "System" }, { 0, "System" },
{ 1, "Авария" }, { 1, "Авария" },
{ 2, "Предупреждение" }, { 2, "Предупреждение" },
{ 3, "Инфо" }, { 3, "Инфо" },
{ 4, "Прочее" } { 4, "Прочее" }
}; };
public TechMessagesController(ITechMessagesRepository techMessagesRepository) public TechMessagesController(ITechMessagesRepository techMessagesRepository)
{ {
this.techMessagesRepository = techMessagesRepository; this.techMessagesRepository = techMessagesRepository;
} }
/// <summary> /// <summary>
/// Получить список технологических сообщений в виде страницы /// Получить список технологических сообщений в виде страницы
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
public async Task<ActionResult<PaginationContainer<TechMessageDto>>> GetPage([FromQuery] PaginationRequest request, CancellationToken token) public async Task<ActionResult<PaginationContainer<TechMessageDto>>> GetPage([FromQuery] PaginationRequest request, CancellationToken token)
{ {
var result = await techMessagesRepository.GetPage(request, token); var result = await techMessagesRepository.GetPage(request, token);
return Ok(result); return Ok(result);
} }
/// <summary> /// <summary>
/// Получить статистику по системам /// Получить статистику по системам
/// </summary> /// </summary>
/// <param name="autoDrillingSystem"></param> /// <param name="autoDrillingSystem"></param>
/// <param name="categoryIds"></param> /// <param name="categoryIds"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("statistics")] [HttpGet("statistics")]
public async Task<ActionResult<IEnumerable<MessagesStatisticDto>>> GetStatistics([FromQuery] IEnumerable<string> autoDrillingSystem, [FromQuery] IEnumerable<int> categoryIds, CancellationToken token) public async Task<ActionResult<IEnumerable<MessagesStatisticDto>>> GetStatistics([FromQuery] IEnumerable<string> autoDrillingSystem, [FromQuery] IEnumerable<int> categoryIds, CancellationToken token)
{ {
var result = await techMessagesRepository.GetStatistics(autoDrillingSystem, categoryIds, token); var result = await techMessagesRepository.GetStatistics(autoDrillingSystem, categoryIds, token);
return Ok(result); return Ok(result);
} }
/// <summary> /// <summary>
/// Получить список всех систем /// Получить список всех систем
/// </summary> /// </summary>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("systems")] [HttpGet("systems")]
public async Task<ActionResult<Dictionary<string, int>>> GetSystems(CancellationToken token) public async Task<ActionResult<Dictionary<string, int>>> GetSystems(CancellationToken token)
{ {
var result = await techMessagesRepository.GetSystems(token); var result = await techMessagesRepository.GetSystems(token);
return Ok(result); return Ok(result);
} }
/// <summary> /// <summary>
/// Получить диапазон дат, для которых есть данные в репозитории /// Получить диапазон дат, для которых есть данные в репозитории
/// </summary> /// </summary>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("range")] [HttpGet("range")]
public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token) public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token)
{ {
var result = await techMessagesRepository.GetDatesRangeAsync(token); var result = await techMessagesRepository.GetDatesRangeAsync(token);
return Ok(result); return Ok(result);
} }
/// <summary> /// <summary>
/// Получить порцию записей, начиная с заданной даты /// Получить порцию записей, начиная с заданной даты
/// </summary> /// </summary>
/// <param name="dateBegin"></param> /// <param name="dateBegin"></param>
/// <param name="take"></param> /// <param name="take"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("part")] [HttpGet("part")]
public async Task<ActionResult<IEnumerable<SetpointLogDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) public async Task<ActionResult<IEnumerable<SetpointLogDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token)
{ {
var result = await techMessagesRepository.GetPart(dateBegin, take, token); var result = await techMessagesRepository.GetPart(dateBegin, take, token);
return Ok(result); return Ok(result);
} }
/// <summary> /// <summary>
/// Добавить новые технологические сообщения /// Добавить новые технологические сообщения
/// </summary> /// </summary>
/// <param name="dtos"></param> /// <param name="dtos"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpPost] [HttpPost]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)] [ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> AddRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) public async Task<IActionResult> AddRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token)
{ {
var userId = User.GetUserId<Guid>(); var userId = User.GetUserId<Guid>();
var result = await techMessagesRepository.AddRange(dtos, userId, token); var result = await techMessagesRepository.AddRange(dtos, userId, token);
return CreatedAtAction(nameof(AddRange), result); return CreatedAtAction(nameof(AddRange), result);
} }
/// <summary> /// <summary>
/// Получить словарь категорий /// Получить словарь категорий
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet("categories")] [HttpGet("categories")]
public ActionResult<Dictionary<int, string>> GetImportantCategories() public ActionResult<Dictionary<int, string>> GetImportantCategories()
{ {
return Ok(categories); return Ok(categories);
} }
} }

View File

@ -28,12 +28,11 @@ public static class DependencyInjection
c.MapType<DateOnly>(() => new OpenApiSchema { Type = "string", Format = "date" }); c.MapType<DateOnly>(() => new OpenApiSchema { Type = "string", Format = "date" });
c.MapType<JsonValue>(() => new OpenApiSchema c.MapType<JsonValue>(() => new OpenApiSchema
{ {
AnyOf = new OpenApiSchema[] AnyOf = [
{
new OpenApiSchema {Type = "string", Format = "string" }, new OpenApiSchema {Type = "string", Format = "string" },
new OpenApiSchema {Type = "number", Format = "int32" }, new OpenApiSchema {Type = "number", Format = "int32" },
new OpenApiSchema {Type = "number", Format = "float" }, new OpenApiSchema {Type = "number", Format = "float" }
} ]
}); });
c.CustomOperationIds(e => c.CustomOperationIds(e =>
@ -45,8 +44,8 @@ public static class DependencyInjection
var needUseKeyCloak = configuration.GetSection("NeedUseKeyCloak").Get<bool>(); var needUseKeyCloak = configuration.GetSection("NeedUseKeyCloak").Get<bool>();
if (needUseKeyCloak) if (needUseKeyCloak)
c.AddKeycloackSecurity(configuration); c.AddKeycloakSecurity(configuration);
else c.AddDefaultSecurity(configuration); else c.AddDefaultSecurity();
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
@ -134,12 +133,12 @@ public static class DependencyInjection
} }
#endregion #endregion
#region Security (Swagger) #region Keycloak
private static void AddKeycloackSecurity(this SwaggerGenOptions options, IConfiguration configuration) private static void AddKeycloakSecurity(this SwaggerGenOptions options, IConfiguration configuration)
{ {
options.AddSecurityDefinition("Keycloack", new OpenApiSecurityScheme options.AddSecurityDefinition("Keycloak", new OpenApiSecurityScheme
{ {
Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345abcdef'", Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345token'",
Name = "Authorization", Name = "Authorization",
In = ParameterLocation.Header, In = ParameterLocation.Header,
Type = SecuritySchemeType.OAuth2, Type = SecuritySchemeType.OAuth2,
@ -147,7 +146,7 @@ public static class DependencyInjection
{ {
Implicit = new OpenApiOAuthFlow Implicit = new OpenApiOAuthFlow
{ {
AuthorizationUrl = new Uri(configuration["Authentication:AuthorizationUrl"]), AuthorizationUrl = new Uri(configuration["Authentication:AuthorizationUrl"]!),
} }
} }
}); });
@ -160,7 +159,7 @@ public static class DependencyInjection
Reference = new OpenApiReference Reference = new OpenApiReference
{ {
Type = ReferenceType.SecurityScheme, Type = ReferenceType.SecurityScheme,
Id = "Keycloack" Id = "Keycloak"
}, },
Scheme = "Bearer", Scheme = "Bearer",
Name = "Bearer", Name = "Bearer",
@ -171,11 +170,11 @@ public static class DependencyInjection
}); });
} }
private static void AddDefaultSecurity(this SwaggerGenOptions options, IConfiguration configuration) private static void AddDefaultSecurity(this SwaggerGenOptions options)
{ {
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{ {
Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345abcdef'", Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345token'",
Name = "Authorization", Name = "Authorization",
In = ParameterLocation.Header, In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey, Type = SecuritySchemeType.ApiKey,

View File

@ -7,9 +7,8 @@ public static class Extensions
{ {
public static T GetUserId<T>(this ClaimsPrincipal principal) public static T GetUserId<T>(this ClaimsPrincipal principal)
{ {
if (principal == null) ArgumentNullException.ThrowIfNull(principal, nameof(principal));
throw new ArgumentNullException(nameof(principal));
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier); var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (String.IsNullOrEmpty(loggedInUserId)) if (String.IsNullOrEmpty(loggedInUserId))

View File

@ -4,29 +4,29 @@ using Refit;
namespace Persistence.Client.Clients namespace Persistence.Client.Clients
{ {
/// <summary> /// <summary>
/// Интерфейс клиента для хранения технологических сообщений /// Интерфейс клиента для хранения технологических сообщений
/// </summary> /// </summary>
public interface ITechMessagesClient public interface ITechMessagesClient
{ {
private const string BaseRoute = "/api/techMessages"; private const string BaseRoute = "/api/techMessages";
[Get($"{BaseRoute}")] [Get($"{BaseRoute}")]
Task<IApiResponse<PaginationContainer<TechMessageDto>>> GetPage([Query] PaginationRequest request, CancellationToken token); Task<IApiResponse<PaginationContainer<TechMessageDto>>> GetPage([Query] PaginationRequest request, CancellationToken token);
[Post($"{BaseRoute}")] [Post($"{BaseRoute}")]
Task<IApiResponse<int>> AddRange([Body] IEnumerable<TechMessageDto> dtos, CancellationToken token); Task<IApiResponse<int>> AddRange([Body] IEnumerable<TechMessageDto> dtos, CancellationToken token);
[Get($"{BaseRoute}/systems")] [Get($"{BaseRoute}/systems")]
Task<IApiResponse<IEnumerable<string>>> GetSystems(CancellationToken token); Task<IApiResponse<IEnumerable<string>>> GetSystems(CancellationToken token);
[Get($"{BaseRoute}/range")] [Get($"{BaseRoute}/range")]
Task<IApiResponse<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token); Task<IApiResponse<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token);
[Get($"{BaseRoute}/part")] [Get($"{BaseRoute}/part")]
Task<IApiResponse<IEnumerable<TechMessageDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token); Task<IApiResponse<IEnumerable<TechMessageDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token);
[Get($"{BaseRoute}/statistics")] [Get($"{BaseRoute}/statistics")]
Task<IApiResponse<IEnumerable<MessagesStatisticDto>>> GetStatistics([Query] string autoDrillingSystem, [Query] int categoryId, CancellationToken token); Task<IApiResponse<IEnumerable<MessagesStatisticDto>>> GetStatistics([Query] string autoDrillingSystem, [Query] int categoryId, CancellationToken token);
} }
} }

View File

@ -1,75 +1,74 @@
using System.IdentityModel.Tokens.Jwt; using Microsoft.Extensions.Configuration;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Persistence.Models.Configurations; using Persistence.Models.Configurations;
using RestSharp; using RestSharp;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Json;
namespace Persistence.Client.Helpers; namespace Persistence.Client.Helpers;
public static class ApiTokenHelper public static class ApiTokenHelper
{ {
public static void Authorize(this HttpClient httpClient, IConfiguration configuration) public static void Authorize(this HttpClient httpClient, IConfiguration configuration)
{ {
var authUser = configuration var authUser = configuration
.GetSection(nameof(AuthUser)) .GetSection(nameof(AuthUser))
.Get<AuthUser>()!; .Get<AuthUser>()!;
var needUseKeyCloak = configuration var needUseKeyCloak = configuration
.GetSection("NeedUseKeyCloak") .GetSection("NeedUseKeyCloak")
.Get<bool>()!; .Get<bool>()!;
var keycloakGetTokenUrl = configuration.GetSection("KeycloakGetTokenUrl").Get<string>() ?? string.Empty; var keycloakGetTokenUrl = configuration.GetSection("KeycloakGetTokenUrl").Get<string>() ?? string.Empty;
var jwtToken = needUseKeyCloak var jwtToken = needUseKeyCloak
? authUser.CreateKeyCloakJwtToken(keycloakGetTokenUrl) ? authUser.CreateKeyCloakJwtToken(keycloakGetTokenUrl)
: authUser.CreateDefaultJwtToken(); : authUser.CreateDefaultJwtToken();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
} }
private static string CreateDefaultJwtToken(this AuthUser authUser) private static string CreateDefaultJwtToken(this AuthUser authUser)
{ {
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(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(), Guid.NewGuid().ToString()) };
};
var tokenDescriptor = new SecurityTokenDescriptor var tokenDescriptor = new SecurityTokenDescriptor
{ {
Issuer = JwtParams.Issuer, Issuer = JwtParams.Issuer,
Audience = JwtParams.Audience, Audience = JwtParams.Audience,
Subject = new ClaimsIdentity(claims), Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddHours(1), Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(JwtParams.SecurityKey, SecurityAlgorithms.HmacSha256Signature) SigningCredentials = new SigningCredentials(JwtParams.SecurityKey, SecurityAlgorithms.HmacSha256Signature)
}; };
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor); var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token); return tokenHandler.WriteToken(token);
} }
private static string CreateKeyCloakJwtToken(this AuthUser authUser, string keycloakGetTokenUrl) private static string CreateKeyCloakJwtToken(this AuthUser authUser, string keycloakGetTokenUrl)
{ {
var restClient = new RestClient(); var restClient = new RestClient();
var request = new RestRequest(keycloakGetTokenUrl, Method.Post); var request = new RestRequest(keycloakGetTokenUrl, Method.Post);
request.AddParameter("username", authUser.Username); request.AddParameter("username", authUser.Username);
request.AddParameter("password", authUser.Password); request.AddParameter("password", authUser.Password);
request.AddParameter("client_id", authUser.ClientId); request.AddParameter("client_id", authUser.ClientId);
request.AddParameter("grant_type", authUser.GrantType); request.AddParameter("grant_type", authUser.GrantType);
var keyCloackResponse = restClient.Post(request); var keycloakResponse = restClient.Post(request);
if (keyCloackResponse.IsSuccessful && !String.IsNullOrEmpty(keyCloackResponse.Content)) if (keycloakResponse.IsSuccessful && !String.IsNullOrEmpty(keycloakResponse.Content))
{ {
var token = JsonSerializer.Deserialize<JwtToken>(keyCloackResponse.Content)!; var token = JsonSerializer.Deserialize<JwtToken>(keycloakResponse.Content)!;
return token.AccessToken; return token.AccessToken;
} }
return String.Empty; return String.Empty;
} }
} }

View File

@ -16,7 +16,7 @@ namespace Persistence.Client
PropertyNameCaseInsensitive = true PropertyNameCaseInsensitive = true
}; };
private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions)); private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions));
private HttpClient httpClient; private readonly HttpClient httpClient;
public PersistenceClientFactory(IHttpClientFactory httpClientFactory, IConfiguration configuration) public PersistenceClientFactory(IHttpClientFactory httpClientFactory, IConfiguration configuration)
{ {
this.httpClient = httpClientFactory.CreateClient(); this.httpClient = httpClientFactory.CreateClient();

View File

@ -1,7 +1,6 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Npgsql;
namespace Persistence.Database.Model; namespace Persistence.Database.Model;

View File

@ -1,8 +1,8 @@
 
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Persistence.Models; using Persistence.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Persistence.Database.Model; namespace Persistence.Database.Model;

View File

@ -3,8 +3,7 @@ using Persistence.Database.Model;
using Xunit; using Xunit;
namespace Persistence.IntegrationTests; namespace Persistence.IntegrationTests;
public abstract class BaseIntegrationTest : IClassFixture<WebAppFactoryFixture>, public abstract class BaseIntegrationTest : IClassFixture<WebAppFactoryFixture>, IDisposable
IDisposable
{ {
protected readonly IServiceScope scope; protected readonly IServiceScope scope;
@ -21,5 +20,6 @@ public abstract class BaseIntegrationTest : IClassFixture<WebAppFactoryFixture>,
{ {
scope.Dispose(); scope.Dispose();
dbContext.Dispose(); dbContext.Dispose();
GC.SuppressFinalize(this);
} }
} }

View File

@ -13,7 +13,7 @@ namespace Persistence.IntegrationTests.Controllers;
public class ChangeLogControllerTest : BaseIntegrationTest public class ChangeLogControllerTest : BaseIntegrationTest
{ {
private readonly IChangeLogClient client; private readonly IChangeLogClient client;
private static Random generatorRandomDigits = new Random(); private static readonly Random generatorRandomDigits = new();
public ChangeLogControllerTest(WebAppFactoryFixture factory) : base(factory) public ChangeLogControllerTest(WebAppFactoryFixture factory) : base(factory)
{ {
@ -28,7 +28,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
{ {
// arrange // arrange
var idDiscriminator = Guid.NewGuid(); var idDiscriminator = Guid.NewGuid();
var dtos = Generate(2, DateTimeOffset.UtcNow); var dtos = Generate(2);
// act // act
var result = await client.ClearAndAddRange(idDiscriminator, dtos); var result = await client.ClearAndAddRange(idDiscriminator, dtos);
@ -52,7 +52,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
// assert // assert
Assert.Equal(HttpStatusCode.OK, result.StatusCode); Assert.Equal(HttpStatusCode.OK, result.StatusCode);
Assert.Equal(insertedCount*2, result.Content); Assert.Equal(insertedCount * 2, result.Content);
} }
[Fact] [Fact]
@ -61,7 +61,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
// arrange // arrange
var count = 1; var count = 1;
var idDiscriminator = Guid.NewGuid(); var idDiscriminator = Guid.NewGuid();
var dtos = Generate(count, DateTimeOffset.UtcNow); var dtos = Generate(count);
var dto = dtos.FirstOrDefault()!; var dto = dtos.FirstOrDefault()!;
// act // act
@ -78,7 +78,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
// arrange // arrange
var count = 3; var count = 3;
var idDiscriminator = Guid.NewGuid(); var idDiscriminator = Guid.NewGuid();
var dtos = Generate(count, DateTimeOffset.UtcNow); var dtos = Generate(count);
// act // act
var result = await client.AddRange(idDiscriminator, dtos); var result = await client.AddRange(idDiscriminator, dtos);
@ -93,7 +93,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
{ {
// arrange // arrange
var idDiscriminator = Guid.NewGuid(); var idDiscriminator = Guid.NewGuid();
var dtos = Generate(1, DateTimeOffset.UtcNow); var dtos = Generate(1);
var dto = dtos.FirstOrDefault()!; var dto = dtos.FirstOrDefault()!;
var result = await client.Add(idDiscriminator, dto); var result = await client.Add(idDiscriminator, dto);
Assert.Equal(HttpStatusCode.Created, result.StatusCode); Assert.Equal(HttpStatusCode.Created, result.StatusCode);
@ -102,7 +102,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
.Where(x => x.IdDiscriminator == idDiscriminator) .Where(x => x.IdDiscriminator == idDiscriminator)
.FirstOrDefault(); .FirstOrDefault();
dto = entity.Adapt<DataWithWellDepthAndSectionDto>(); dto = entity.Adapt<DataWithWellDepthAndSectionDto>();
dto.DepthEnd = dto.DepthEnd + 10; dto.DepthEnd += 10;
// act // act
result = await client.Update(dto); result = await client.Update(dto);
@ -143,7 +143,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
{ {
// arrange // arrange
var count = 2; var count = 2;
var dtos = Generate(count, DateTimeOffset.UtcNow); var dtos = Generate(count);
var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray(); var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray();
dbContext.ChangeLog.AddRange(entities); dbContext.ChangeLog.AddRange(entities);
dbContext.SaveChanges(); dbContext.SaveChanges();
@ -169,7 +169,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
public async Task Delete_returns_success() public async Task Delete_returns_success()
{ {
// arrange // arrange
var dtos = Generate(1, DateTimeOffset.UtcNow); var dtos = Generate(1);
var dto = dtos.FirstOrDefault()!; var dto = dtos.FirstOrDefault()!;
var entity = dto.Adapt<ChangeLog>(); var entity = dto.Adapt<ChangeLog>();
dbContext.ChangeLog.Add(entity); dbContext.ChangeLog.Add(entity);
@ -188,7 +188,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
{ {
// arrange // arrange
var count = 10; var count = 10;
var dtos = Generate(count, DateTimeOffset.UtcNow); var dtos = Generate(count);
var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray(); var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray();
dbContext.ChangeLog.AddRange(entities); dbContext.ChangeLog.AddRange(entities);
dbContext.SaveChanges(); dbContext.SaveChanges();
@ -248,8 +248,8 @@ public class ChangeLogControllerTest : BaseIntegrationTest
var filterRequest = new SectionPartRequest() var filterRequest = new SectionPartRequest()
{ {
DepthStart = 0, DepthStart = 0,
DepthEnd = 1000, DepthEnd = 1000,
}; };
var paginationRequest = new PaginationRequest() var paginationRequest = new PaginationRequest()
@ -294,7 +294,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
foreach (var entity in entities) foreach (var entity in entities)
{ {
entity.DepthEnd = entity.DepthEnd + 10; entity.DepthEnd += 10;
} }
var dtos = entities.Select(e => e.Adapt<DataWithWellDepthAndSectionDto>()).ToArray(); var dtos = entities.Select(e => e.Adapt<DataWithWellDepthAndSectionDto>()).ToArray();
await client.UpdateRange(dtos); await client.UpdateRange(dtos);
@ -311,7 +311,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
} }
private static IEnumerable<DataWithWellDepthAndSectionDto> Generate(int count, DateTimeOffset from) private static IEnumerable<DataWithWellDepthAndSectionDto> Generate(int count)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
yield return new DataWithWellDepthAndSectionDto() yield return new DataWithWellDepthAndSectionDto()
@ -334,7 +334,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
var maxDayCount = daysRange.Item2; var maxDayCount = daysRange.Item2;
Guid idDiscriminator = Guid.NewGuid(); Guid idDiscriminator = Guid.NewGuid();
var dtos = Generate(count, DateTimeOffset.UtcNow); var dtos = Generate(count);
var entities = dtos.Select(d => var entities = dtos.Select(d =>
{ {
var entity = d.Adapt<ChangeLog>(); var entity = d.Adapt<ChangeLog>();

View File

@ -28,7 +28,7 @@ public class DataSaubControllerTest : TimeSeriesBaseControllerTest<DataSaub, Dat
WellDepth = 18, WellDepth = 18,
}; };
private readonly DataSaub entity = new DataSaub() private readonly DataSaub entity = new()
{ {
AxialLoad = 1, AxialLoad = 1,
BitDepth = 2, BitDepth = 2,

View File

@ -9,11 +9,11 @@ namespace Persistence.IntegrationTests.Controllers
{ {
public class SetpointControllerTest : BaseIntegrationTest public class SetpointControllerTest : BaseIntegrationTest
{ {
private ISetpointClient setpointClient; private readonly ISetpointClient setpointClient;
private class TestObject private class TestObject
{ {
public string? value1 { get; set; } public string? Value1 { get; set; }
public int? value2 { get; set; } public int? Value2 { get; set; }
} }
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory) public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
{ {
@ -224,8 +224,8 @@ namespace Persistence.IntegrationTests.Controllers
var setpointKey = Guid.NewGuid(); var setpointKey = Guid.NewGuid();
var setpointValue = new TestObject() var setpointValue = new TestObject()
{ {
value1 = "1", Value1 = "1",
value2 = 2 Value2 = 2
}; };
//act //act

View File

@ -1,289 +1,289 @@
using System.Net; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Persistence.Client; using Persistence.Client;
using Persistence.Client.Clients; using Persistence.Client.Clients;
using Persistence.Database.Entity; using Persistence.Database.Entity;
using Persistence.Models; using Persistence.Models;
using Persistence.Models.Requests; using Persistence.Models.Requests;
using System.Net;
using Xunit; using Xunit;
namespace Persistence.IntegrationTests.Controllers namespace Persistence.IntegrationTests.Controllers
{ {
public class TechMessagesControllerTest : BaseIntegrationTest public class TechMessagesControllerTest : BaseIntegrationTest
{ {
private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey"; private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey";
private readonly ITechMessagesClient techMessagesClient; private readonly ITechMessagesClient techMessagesClient;
private readonly IMemoryCache memoryCache; private readonly IMemoryCache memoryCache;
public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory) public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory)
{ {
var scope = factory.Services.CreateScope(); var scope = factory.Services.CreateScope();
var persistenceClientFactory = scope.ServiceProvider var persistenceClientFactory = scope.ServiceProvider
.GetRequiredService<PersistenceClientFactory>(); .GetRequiredService<PersistenceClientFactory>();
techMessagesClient = persistenceClientFactory.GetClient<ITechMessagesClient>(); techMessagesClient = persistenceClientFactory.GetClient<ITechMessagesClient>();
memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>(); memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
} }
[Fact] [Fact]
public async Task GetPage_returns_success() public async Task GetPage_returns_success()
{ {
//arrange //arrange
memoryCache.Remove(SystemCacheKey); memoryCache.Remove(SystemCacheKey);
dbContext.CleanupDbSet<TechMessage>(); dbContext.CleanupDbSet<TechMessage>();
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>(); dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
var PaginationRequest = new PaginationRequest() var PaginationRequest = new PaginationRequest()
{ {
Skip = 1, Skip = 1,
Take = 2, Take = 2,
SortSettings = nameof(TechMessage.CategoryId) SortSettings = nameof(TechMessage.CategoryId)
}; };
//act //act
var response = await techMessagesClient.GetPage(PaginationRequest, new CancellationToken()); var response = await techMessagesClient.GetPage(PaginationRequest, new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.Empty(response.Content.Items); Assert.Empty(response.Content.Items);
Assert.Equal(PaginationRequest.Skip, response.Content.Skip); Assert.Equal(PaginationRequest.Skip, response.Content.Skip);
Assert.Equal(PaginationRequest.Take, response.Content.Take); Assert.Equal(PaginationRequest.Take, response.Content.Take);
} }
[Fact] [Fact]
public async Task GetPage_AfterSave_returns_success() public async Task GetPage_AfterSave_returns_success()
{ {
//arrange //arrange
var dtos = await InsertRange(); var dtos = await InsertRange();
var dtosCount = dtos.Count(); var dtosCount = dtos.Count();
var PaginationRequest = new PaginationRequest() var PaginationRequest = new PaginationRequest()
{ {
Skip = 0, Skip = 0,
Take = 2, Take = 2,
SortSettings = nameof(TechMessage.CategoryId) SortSettings = nameof(TechMessage.CategoryId)
}; };
//act //act
var response = await techMessagesClient.GetPage(PaginationRequest, new CancellationToken()); var response = await techMessagesClient.GetPage(PaginationRequest, new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.Equal(dtosCount, response.Content.Count); Assert.Equal(dtosCount, response.Content.Count);
} }
[Fact] [Fact]
public async Task InsertRange_returns_success() public async Task InsertRange_returns_success()
{ {
await InsertRange(); await InsertRange();
} }
[Fact] [Fact]
public async Task InsertRange_returns_BadRequest() public async Task InsertRange_returns_BadRequest()
{ {
//arrange //arrange
var dtos = new List<TechMessageDto>() var dtos = new List<TechMessageDto>()
{ {
new TechMessageDto() new()
{ {
EventId = Guid.NewGuid(), EventId = Guid.NewGuid(),
CategoryId = -1, // < 0 CategoryId = -1, // < 0
Timestamp = DateTimeOffset.UtcNow, Timestamp = DateTimeOffset.UtcNow,
Depth = -1, // < 0 Depth = -1, // < 0
MessageText = string.Empty, // length < 0 MessageText = string.Empty, // length < 0
System = string.Concat(Enumerable.Repeat(nameof(TechMessageDto.System), 100)), // length > 256 System = string.Concat(Enumerable.Repeat(nameof(TechMessageDto.System), 100)), // length > 256
UserId = Guid.NewGuid() UserId = Guid.NewGuid()
} }
}; };
//act //act
var response = await techMessagesClient.AddRange(dtos, new CancellationToken()); var response = await techMessagesClient.AddRange(dtos, new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
} }
[Fact] [Fact]
public async Task GetSystems_returns_success() public async Task GetSystems_returns_success()
{ {
//arrange //arrange
memoryCache.Remove(SystemCacheKey); memoryCache.Remove(SystemCacheKey);
dbContext.CleanupDbSet<TechMessage>(); dbContext.CleanupDbSet<TechMessage>();
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>(); dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
//act //act
var response = await techMessagesClient.GetSystems(new CancellationToken()); var response = await techMessagesClient.GetSystems(new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.Empty(response.Content); Assert.Empty(response.Content);
} }
[Fact] [Fact]
public async Task GetSystems_AfterSave_returns_success() public async Task GetSystems_AfterSave_returns_success()
{ {
//arrange //arrange
var dtos = await InsertRange(); var dtos = await InsertRange();
var systems = dtos var systems = dtos
.Select(e => e.System) .Select(e => e.System)
.Distinct() .Distinct()
.ToArray(); .ToArray();
//act //act
var response = await techMessagesClient.GetSystems(new CancellationToken()); var response = await techMessagesClient.GetSystems(new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
string?[]? content = response.Content?.ToArray(); string?[]? content = response.Content?.ToArray();
Assert.Equal(systems, content); Assert.Equal(systems, content);
} }
[Fact] [Fact]
public async Task GetStatistics_returns_success() public async Task GetStatistics_returns_success()
{ {
//arrange //arrange
memoryCache.Remove(SystemCacheKey); memoryCache.Remove(SystemCacheKey);
dbContext.CleanupDbSet<TechMessage>(); dbContext.CleanupDbSet<TechMessage>();
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>(); dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
var imortantId = 1; var importantId = 1;
var autoDrillingSystem = nameof(TechMessageDto.System); var autoDrillingSystem = nameof(TechMessageDto.System);
//act //act
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken()); var response = await techMessagesClient.GetStatistics(autoDrillingSystem, importantId, new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.Empty(response.Content); Assert.Empty(response.Content);
} }
[Fact] [Fact]
public async Task GetStatistics_AfterSave_returns_success() public async Task GetStatistics_AfterSave_returns_success()
{ {
//arrange //arrange
var imortantId = 0; var importantId = 0;
var autoDrillingSystem = nameof(TechMessageDto.System); var autoDrillingSystem = nameof(TechMessageDto.System);
var dtos = await InsertRange(); var dtos = await InsertRange();
var filteredDtos = dtos.Where(e => e.CategoryId == imortantId && e.System == autoDrillingSystem); var filteredDtos = dtos.Where(e => e.CategoryId == importantId && e.System == autoDrillingSystem);
//act //act
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken()); var response = await techMessagesClient.GetStatistics(autoDrillingSystem, importantId, new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
var categories = response.Content var categories = response.Content
.FirstOrDefault()?.Categories .FirstOrDefault()?.Categories
.FirstOrDefault(e => e.Key == 0).Value; .FirstOrDefault(e => e.Key == 0).Value;
Assert.Equal(filteredDtos.Count(), categories); Assert.Equal(filteredDtos.Count(), categories);
} }
[Fact] [Fact]
public async Task GetDatesRange_returns_success() public async Task GetDatesRange_returns_success()
{ {
//act //act
var response = await techMessagesClient.GetDatesRangeAsync(new CancellationToken()); var response = await techMessagesClient.GetDatesRangeAsync(new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
//Assert.Equal(DateTimeOffset.MinValue, response.Content?.From); //Assert.Equal(DateTimeOffset.MinValue, response.Content?.From);
//Assert.Equal(DateTimeOffset.MaxValue, response.Content?.To); //Assert.Equal(DateTimeOffset.MaxValue, response.Content?.To);
} }
[Fact] [Fact]
public async Task GetDatesRange_AfterSave_returns_success() public async Task GetDatesRange_AfterSave_returns_success()
{ {
//arrange //arrange
await InsertRange(); await InsertRange();
//act //act
var response = await techMessagesClient.GetDatesRangeAsync(new CancellationToken()); var response = await techMessagesClient.GetDatesRangeAsync(new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.NotNull(response.Content?.From); Assert.NotNull(response.Content?.From);
Assert.NotNull(response.Content?.To); Assert.NotNull(response.Content?.To);
} }
[Fact] [Fact]
public async Task GetPart_returns_success() public async Task GetPart_returns_success()
{ {
//arrange //arrange
var dateBegin = DateTimeOffset.UtcNow; var dateBegin = DateTimeOffset.UtcNow;
var take = 2; var take = 2;
//act //act
var response = await techMessagesClient.GetPart(dateBegin, take, new CancellationToken()); var response = await techMessagesClient.GetPart(dateBegin, take, new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.Empty(response.Content); Assert.Empty(response.Content);
} }
[Fact] [Fact]
public async Task GetPart_AfterSave_returns_success() public async Task GetPart_AfterSave_returns_success()
{ {
//arrange //arrange
var dateBegin = DateTimeOffset.UtcNow; var dateBegin = DateTimeOffset.UtcNow;
var take = 1; var take = 1;
await InsertRange(); await InsertRange();
//act //act
var response = await techMessagesClient.GetPart(dateBegin, take, new CancellationToken()); var response = await techMessagesClient.GetPart(dateBegin, take, new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content); Assert.NotNull(response.Content);
Assert.NotEmpty(response.Content); Assert.NotEmpty(response.Content);
} }
private async Task<IEnumerable<TechMessageDto>> InsertRange() private async Task<IEnumerable<TechMessageDto>> InsertRange()
{ {
//arrange //arrange
memoryCache.Remove(SystemCacheKey); memoryCache.Remove(SystemCacheKey);
dbContext.CleanupDbSet<TechMessage>(); dbContext.CleanupDbSet<TechMessage>();
dbContext.CleanupDbSet<DrillingSystem>(); dbContext.CleanupDbSet<DrillingSystem>();
var dtos = new List<TechMessageDto>() var dtos = new List<TechMessageDto>()
{ {
new TechMessageDto() new()
{ {
EventId = Guid.NewGuid(), EventId = Guid.NewGuid(),
CategoryId = 1, CategoryId = 1,
Timestamp = DateTimeOffset.UtcNow, Timestamp = DateTimeOffset.UtcNow,
Depth = 1.11, Depth = 1.11,
MessageText = nameof(TechMessageDto.MessageText), MessageText = nameof(TechMessageDto.MessageText),
System = nameof(TechMessageDto.System).ToLower(), System = nameof(TechMessageDto.System).ToLower(),
UserId = Guid.NewGuid() UserId = Guid.NewGuid()
}, },
new TechMessageDto() new()
{ {
EventId = Guid.NewGuid(), EventId = Guid.NewGuid(),
CategoryId = 2, CategoryId = 2,
Timestamp = DateTimeOffset.UtcNow, Timestamp = DateTimeOffset.UtcNow,
Depth = 2.22, Depth = 2.22,
MessageText = nameof(TechMessageDto.MessageText), MessageText = nameof(TechMessageDto.MessageText),
System = nameof(TechMessageDto.System).ToLower(), System = nameof(TechMessageDto.System).ToLower(),
UserId = Guid.NewGuid() UserId = Guid.NewGuid()
} }
}; };
//act //act
var response = await techMessagesClient.AddRange(dtos, new CancellationToken()); var response = await techMessagesClient.AddRange(dtos, new CancellationToken());
//assert //assert
Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.Equal(HttpStatusCode.Created, response.StatusCode);
Assert.Equal(dtos.Count, response.Content); Assert.Equal(dtos.Count, response.Content);
return dtos; return dtos;
} }
} }
} }

View File

@ -12,7 +12,7 @@ public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrat
where TEntity : class, ITimestampedData, new() where TEntity : class, ITimestampedData, new()
where TDto : class, new() where TDto : class, new()
{ {
private ITimeSeriesClient<TDto> timeSeriesClient; private readonly ITimeSeriesClient<TDto> timeSeriesClient;
public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory) public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory)
{ {
@ -31,7 +31,7 @@ public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrat
var expected = dto.Adapt<TDto>(); var expected = dto.Adapt<TDto>();
//act //act
var response = await timeSeriesClient.AddRange(new TDto[] { expected }); var response = await timeSeriesClient.AddRange([expected]);
//assert //assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -116,7 +116,7 @@ public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrat
} }
else else
{ {
Assert.Equal(entities.Count(), response.Content.Count()); Assert.Equal(entities.Count, response.Content.Count());
} }

View File

@ -111,7 +111,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest
Guid idDiscriminator = Guid.NewGuid(); Guid idDiscriminator = Guid.NewGuid();
int count = 10; int count = 10;
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
var insertResponse = await client.AddRange(idDiscriminator, testSets); await client.AddRange(idDiscriminator, testSets);
var expectedCount = count / 2; var expectedCount = count / 2;
// act // act
@ -133,7 +133,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest
var expectedCount = 1; var expectedCount = 1;
int count = 10 + expectedCount; int count = 10 + expectedCount;
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
var insertResponse = await client.AddRange(idDiscriminator, testSets); await client.AddRange(idDiscriminator, testSets);
// act // act
var response = await client.Get(idDiscriminator, null, null, count - expectedCount, count); var response = await client.Get(idDiscriminator, null, null, count - expectedCount, count);
@ -174,7 +174,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest
var dateMin = DateTimeOffset.Now; var dateMin = DateTimeOffset.Now;
var dateMax = DateTimeOffset.Now.AddSeconds(count - 1); var dateMax = DateTimeOffset.Now.AddSeconds(count - 1);
IEnumerable<TimestampedSetDto> testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); IEnumerable<TimestampedSetDto> testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7)));
var insertResponse = await client.AddRange(idDiscriminator, testSets); await client.AddRange(idDiscriminator, testSets);
var tolerance = TimeSpan.FromSeconds(1); var tolerance = TimeSpan.FromSeconds(1);
// act // act
@ -195,7 +195,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest
Guid idDiscriminator = Guid.NewGuid(); Guid idDiscriminator = Guid.NewGuid();
int count = 144; int count = 144;
IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7)));
var insertResponse = await client.AddRange(idDiscriminator, testSets); await client.AddRange(idDiscriminator, testSets);
// act // act
var response = await client.Count(idDiscriminator); var response = await client.Count(idDiscriminator);

View File

@ -60,5 +60,7 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
.Options); .Options);
await dbContext.Database.EnsureDeletedAsync(); await dbContext.Database.EnsureDeletedAsync();
GC.SuppressFinalize(dbContext);
} }
} }

View File

@ -44,7 +44,7 @@ public static class EFExtensionsSortBy
var name = propertyInfo.Name.ToLower(); var name = propertyInfo.Name.ToLower();
ParameterExpression arg = Expression.Parameter(type, "x"); ParameterExpression arg = Expression.Parameter(type, "x");
MemberExpression property = Expression.Property(arg, propertyInfo.Name); MemberExpression property = Expression.Property(arg, propertyInfo.Name);
var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); var selector = Expression.Lambda(property, [ arg ]);
var typeAccessor = new TypeAccessor var typeAccessor = new TypeAccessor
{ {
KeySelector = selector, KeySelector = selector,
@ -261,7 +261,7 @@ public static class EFExtensionsSortBy
: orderByAscending; : orderByAscending;
var newQuery = (IOrderedQueryable<TSource>)genericMethod var newQuery = (IOrderedQueryable<TSource>)genericMethod
.Invoke(genericMethod, [ query, lambdaExpression ])!; .Invoke(genericMethod, [query, lambdaExpression])!;
return newQuery; return newQuery;
} }
} }

View File

@ -29,7 +29,7 @@ public static class QueryBuilders
return query; return query;
} }
public static IQueryable<TEntity> Apply<TEntity>(this IQueryable<TEntity> query,DateTimeOffset momentUtc) public static IQueryable<TEntity> Apply<TEntity>(this IQueryable<TEntity> query, DateTimeOffset momentUtc)
where TEntity : class, IChangeLog where TEntity : class, IChangeLog
{ {
momentUtc = momentUtc.ToUniversalTime(); momentUtc = momentUtc.ToUniversalTime();
@ -40,7 +40,7 @@ public static class QueryBuilders
return query; 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,

View File

@ -9,7 +9,7 @@ using UuidExtensions;
namespace Persistence.Repository.Repositories; namespace Persistence.Repository.Repositories;
public class ChangeLogRepository : IChangeLogRepository public class ChangeLogRepository : IChangeLogRepository
{ {
private DbContext db; private readonly DbContext db;
public ChangeLogRepository(DbContext db) public ChangeLogRepository(DbContext db)
{ {
@ -123,7 +123,7 @@ public class ChangeLogRepository : IChangeLogRepository
return result; return result;
} }
public async Task<PaginationContainer<DataWithWellDepthAndSectionDto>> GetByDate( public async Task<PaginationContainer<DataWithWellDepthAndSectionDto>> GetByDate(

View File

@ -5,174 +5,174 @@ using Persistence.Database.Entity;
using Persistence.Models; using Persistence.Models;
using Persistence.Models.Requests; using Persistence.Models.Requests;
using Persistence.Repositories; using Persistence.Repositories;
using Persistence.Repository.Extensions; using UuidExtensions;
namespace Persistence.Repository.Repositories namespace Persistence.Repository.Repositories
{ {
public class TechMessagesRepository : ITechMessagesRepository public class TechMessagesRepository : ITechMessagesRepository
{ {
private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey"; private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey";
private const int CacheExpirationInMinutes = 60; private const int CacheExpirationInMinutes = 60;
private readonly IMemoryCache memoryCache; private readonly IMemoryCache memoryCache;
private DbContext db; private readonly DbContext db;
public TechMessagesRepository(DbContext db, IMemoryCache memoryCache) public TechMessagesRepository(DbContext db, IMemoryCache memoryCache)
{ {
this.memoryCache = memoryCache; this.memoryCache = memoryCache;
this.db = db; this.db = db;
} }
protected virtual 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)
{ {
var query = GetQueryReadOnly(); var query = GetQueryReadOnly();
var count = await query.CountAsync(token); var count = await query.CountAsync(token);
var sort = request.SortSettings != string.Empty var sort = request.SortSettings != string.Empty
? request.SortSettings ? request.SortSettings!
: nameof(TechMessage.Timestamp); : nameof(TechMessage.Timestamp);
var entities = await query var entities = await query
.SortBy(request.SortSettings) .SortBy(sort)
.Skip(request.Skip) .Skip(request.Skip)
.Take(request.Take) .Take(request.Take)
.ToArrayAsync(token); .ToArrayAsync(token);
var dto = new PaginationContainer<TechMessageDto>() var dto = new PaginationContainer<TechMessageDto>()
{ {
Skip = request.Skip, Skip = request.Skip,
Take = request.Take, Take = request.Take,
Count = count, Count = count,
Items = entities.Select(e => e.Adapt<TechMessageDto>()) Items = entities.Select(e => e.Adapt<TechMessageDto>())
}; };
return dto; return dto;
} }
public async Task<IEnumerable<MessagesStatisticDto>> GetStatistics(IEnumerable<string> autoDrillingSystem, IEnumerable<int> categoryIds, CancellationToken token) public async Task<IEnumerable<MessagesStatisticDto>> GetStatistics(IEnumerable<string> autoDrillingSystem, IEnumerable<int> categoryIds, CancellationToken token)
{ {
var query = GetQueryReadOnly(); var query = GetQueryReadOnly();
var systems = autoDrillingSystem.Select(s => s.ToLower().Trim()); var systems = autoDrillingSystem.Select(s => s.ToLower().Trim());
var result = await query var result = await query
.Where(e => systems.Count() == 0 || systems.Contains(e.System.Name.ToLower().Trim())) .Where(e => !systems.Any() || systems.Contains(e.System.Name.ToLower().Trim()))
.GroupBy(e => e.System.Name, (key, group) => new .GroupBy(e => e.System.Name, (key, group) => new
{ {
System = key, System = key,
Categories = group Categories = group
.Where(g => categoryIds.Count() == 0 || categoryIds.Contains(g.CategoryId)) .Where(g => !categoryIds.Any() || categoryIds.Contains(g.CategoryId))
}) })
.ToArrayAsync(token); .ToArrayAsync(token);
var entities = new List<MessagesStatisticDto>(); var entities = new List<MessagesStatisticDto>();
foreach (var e in result) foreach (var e in result)
{ {
var categories = e.Categories var categories = e.Categories
.GroupBy(g => g.CategoryId) .GroupBy(g => g.CategoryId)
.ToDictionary(c => c.Key, v => v.Count()); .ToDictionary(c => c.Key, v => v.Count());
var entity = new MessagesStatisticDto() var entity = new MessagesStatisticDto()
{ {
System = e.System, System = e.System,
Categories = categories Categories = categories
}; };
entities.Add(entity); entities.Add(entity);
} }
return entities; return entities;
} }
public async Task<IEnumerable<string>> GetSystems(CancellationToken token) public async Task<IEnumerable<string>> GetSystems(CancellationToken token)
{ {
var entities = await GetDrillingSystems(token); var entities = await GetDrillingSystems(token);
var result = entities.Select(e => e.Name); var result = entities.Select(e => e.Name);
return result; return result;
} }
public async Task<int> AddRange(IEnumerable<TechMessageDto> dtos, Guid userId, CancellationToken token) public async Task<int> AddRange(IEnumerable<TechMessageDto> dtos, Guid userId, CancellationToken token)
{ {
var entities = new List<TechMessage>(); var entities = new List<TechMessage>();
foreach (var dto in dtos) foreach (var dto in dtos)
{ {
var entity = dto.Adapt<TechMessage>(); var entity = dto.Adapt<TechMessage>();
var systems = await GetDrillingSystems(token); var systems = await GetDrillingSystems(token);
var systemId = systems.FirstOrDefault(e => e.Name.ToLower().Trim() == dto.System.ToLower().Trim())?.SystemId var systemId = systems.FirstOrDefault(e => e.Name.ToLower().Trim() == dto.System.ToLower().Trim())?.SystemId
?? await CreateDrillingSystem(dto.System, token); ?? await CreateDrillingSystem(dto.System, token);
entity.SystemId = systemId; entity.SystemId = systemId;
entity.UserId = userId; entity.UserId = userId;
entities.Add(entity); entities.Add(entity);
} }
await db.Set<TechMessage>().AddRangeAsync(entities, token); await db.Set<TechMessage>().AddRangeAsync(entities, token);
var result = await db.SaveChangesAsync(token); var result = await db.SaveChangesAsync(token);
return result; return result;
} }
public async Task<IEnumerable<TechMessageDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) public async Task<IEnumerable<TechMessageDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token)
{ {
var query = GetQueryReadOnly(); var query = GetQueryReadOnly();
var entities = await query var entities = await query
.Where(e => e.Timestamp >= dateBegin) .Where(e => e.Timestamp >= dateBegin)
.Take(take) .Take(take)
.ToArrayAsync(token); .ToArrayAsync(token);
var dtos = entities var dtos = entities
.Select(e => e.Adapt<TechMessageDto>()); .Select(e => e.Adapt<TechMessageDto>());
return dtos; return dtos;
} }
public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token) public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token)
{ {
var query = GetQueryReadOnly() var query = GetQueryReadOnly()
.GroupBy(e => 1) .GroupBy(e => 1)
.Select(group => new .Select(group => new
{ {
Min = group.Min(e => e.Timestamp), Min = group.Min(e => e.Timestamp),
Max = group.Max(e => e.Timestamp), Max = group.Max(e => e.Timestamp),
}); });
var values = await query.FirstOrDefaultAsync(token); var values = await query.FirstOrDefaultAsync(token);
var result = new DatesRangeDto() var result = new DatesRangeDto()
{ {
From = values?.Min ?? DateTimeOffset.MinValue, From = values?.Min ?? DateTimeOffset.MinValue,
To = values?.Max ?? DateTimeOffset.MaxValue To = values?.Max ?? DateTimeOffset.MaxValue
}; };
return result; return result;
} }
private async Task<IEnumerable<Models.DrillingSystemDto>> GetDrillingSystems(CancellationToken token) private async Task<IEnumerable<Models.DrillingSystemDto>> GetDrillingSystems(CancellationToken token)
{ {
var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async f => var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async f =>
{ {
f.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CacheExpirationInMinutes); f.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CacheExpirationInMinutes);
var query = db.Set<Database.Entity.DrillingSystem>(); var query = db.Set<Database.Entity.DrillingSystem>();
var entities = await query.ToListAsync(token); var entities = await query.ToListAsync(token);
var dtos = entities.Select(e => e.Adapt<Models.DrillingSystemDto>()); var dtos = entities.Select(e => e.Adapt<Models.DrillingSystemDto>());
return dtos; return dtos;
}); });
return systems!; return systems!;
} }
private async Task<Guid> CreateDrillingSystem(string name, CancellationToken token) private async Task<Guid> CreateDrillingSystem(string name, CancellationToken token)
{ {
memoryCache.Remove(SystemCacheKey); memoryCache.Remove(SystemCacheKey);
var entity = new Database.Entity.DrillingSystem() var entity = new DrillingSystem()
{ {
SystemId = default, SystemId = Uuid7.Guid(),
Name = name.ToLower().Trim() Name = name.ToLower().Trim()
}; };
await db.Set<Database.Entity.DrillingSystem>().AddAsync(entity); await db.Set<DrillingSystem>().AddAsync(entity, token);
await db.SaveChangesAsync(token); await db.SaveChangesAsync(token);
return entity.SystemId; return entity.SystemId;
} }
} }
} }

View File

@ -9,7 +9,7 @@ public class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataRepository
where TEntity : class, ITimestampedData, new() where TEntity : class, ITimestampedData, new()
where TDto : class, ITimeSeriesAbstractDto, new() where TDto : class, ITimeSeriesAbstractDto, new()
{ {
private DbContext db; private readonly DbContext db;
public TimeSeriesDataRepository(DbContext db) public TimeSeriesDataRepository(DbContext db)
{ {

View File

@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Persistence.Models;
namespace Persistence.API; namespace Persistence.API;

View File

@ -1,16 +1,11 @@
using System; using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Persistence; namespace Persistence;
public static class EFExtensions public static class EFExtensions
{ {
struct TypeAcessor struct TypeAccessor
{ {
public LambdaExpression KeySelector { get; set; } public LambdaExpression KeySelector { get; set; }
public MethodInfo OrderBy { get; set; } public MethodInfo OrderBy { get; set; }
@ -25,7 +20,7 @@ public static class EFExtensions
private static readonly MethodInfo methodThenBy = GetExtOrderMethod("ThenBy"); private static readonly MethodInfo methodThenBy = GetExtOrderMethod("ThenBy");
private static readonly MethodInfo methodThenByDescending = GetExtOrderMethod("ThenByDescending"); private static readonly MethodInfo methodThenByDescending = GetExtOrderMethod("ThenByDescending");
private static ConcurrentDictionary<Type, Dictionary<string, TypeAcessor>> TypePropSelectors { get; set; } = new(); private static ConcurrentDictionary<Type, Dictionary<string, TypeAccessor>> TypePropSelectors { get; set; } = new();
private static MethodInfo GetExtOrderMethod(string methodName) private static MethodInfo GetExtOrderMethod(string methodName)
=> typeof(System.Linq.Queryable) => typeof(System.Linq.Queryable)
@ -35,17 +30,17 @@ public static class EFExtensions
m.GetParameters().Length == 2 && m.GetParameters().Length == 2 &&
m.GetParameters()[1].ParameterType.IsAssignableTo(typeof(LambdaExpression))) m.GetParameters()[1].ParameterType.IsAssignableTo(typeof(LambdaExpression)))
.Single(); .Single();
private static Dictionary<string, TypeAcessor> MakeTypeAcessors(Type type) private static Dictionary<string, TypeAccessor> MakeTypeAccessors(Type type)
{ {
var propContainer = new Dictionary<string, TypeAcessor>(); var propContainer = new Dictionary<string, TypeAccessor>();
var properties = type.GetProperties(); var properties = type.GetProperties();
foreach (var propertyInfo in properties) foreach (var propertyInfo in properties)
{ {
var name = propertyInfo.Name.ToLower(); var name = propertyInfo.Name.ToLower();
ParameterExpression arg = Expression.Parameter(type, "x"); ParameterExpression arg = Expression.Parameter(type, "x");
MemberExpression property = Expression.Property(arg, propertyInfo.Name); MemberExpression property = Expression.Property(arg, propertyInfo.Name);
var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); var selector = Expression.Lambda(property, [arg]);
var typeAccessor = new TypeAcessor var typeAccessor = new TypeAccessor
{ {
KeySelector = selector, KeySelector = selector,
OrderBy = methodOrderBy.MakeGenericMethod(type, propertyInfo.PropertyType), OrderBy = methodOrderBy.MakeGenericMethod(type, propertyInfo.PropertyType),
@ -99,16 +94,16 @@ public static class EFExtensions
string propertyName, string propertyName,
bool isDesc) bool isDesc)
{ {
var typePropSelector = TypePropSelectors.GetOrAdd(typeof(TSource), MakeTypeAcessors); var typePropSelector = TypePropSelectors.GetOrAdd(typeof(TSource), MakeTypeAccessors);
var propertyNamelower = propertyName.ToLower(); var propertyNameLower = propertyName.ToLower();
var typeAccessor = typePropSelector[propertyNamelower]; var typeAccessor = typePropSelector[propertyNameLower];
var genericMethod = isDesc var genericMethod = isDesc
? typeAccessor.OrderByDescending ? typeAccessor.OrderByDescending
: typeAccessor.OrderBy; : typeAccessor.OrderBy;
var newQuery = (IOrderedQueryable<TSource>)genericMethod var newQuery = (IOrderedQueryable<TSource>)genericMethod
.Invoke(genericMethod, new object[] { query, typeAccessor.KeySelector })!; .Invoke(genericMethod, [query, typeAccessor.KeySelector])!;
return newQuery; return newQuery;
} }
} }

View File

@ -1,10 +1,4 @@
using System; namespace Persistence.Models;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Persistence.Models;
/// <summary> /// <summary>
/// Dto для хранения записей, содержащих начальную и конечную глубину забоя, а также секцию /// Dto для хранения записей, содержащих начальную и конечную глубину забоя, а также секцию

View File

@ -11,7 +11,7 @@ public class PaginationContainer<T>
/// </summary> /// </summary>
public PaginationContainer() public PaginationContainer()
{ {
Items = Enumerable.Empty<T>(); Items = [];
} }
/// <summary> /// <summary>

View File

@ -1,6 +1,4 @@
using Persistence.Models; namespace Persistence.Repositories;
namespace Persistence.Repositories;
/// <summary> /// <summary>
/// Интерфейс по работе с прореженными данными /// Интерфейс по работе с прореженными данными