Изменить TechMessagesRepository, добавить тестирование валидации

This commit is contained in:
Roman Efremov 2024-11-28 13:13:07 +05:00
parent 1e87523ab9
commit 097b422310
9 changed files with 110 additions and 82 deletions

View File

@ -1,4 +1,5 @@
using Persistence.Models;
using Microsoft.AspNetCore.Mvc;
using Persistence.Models;
using Refit;
namespace Persistence.Client.Clients
@ -19,7 +20,7 @@ namespace Persistence.Client.Clients
[Get($"{BaseRoute}/systems")]
Task<IApiResponse<IEnumerable<string>>> GetSystems(CancellationToken token);
[Get($"{BaseRoute}/statistics")]
Task<IApiResponse<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token);
[Get($"{BaseRoute}/statistics/" + "{autoDrillingSystem}")]
Task<IApiResponse<int>> GetStatistics(string? autoDrillingSystem, int? importantId, CancellationToken token);
}
}

View File

@ -12,7 +12,7 @@ using Persistence.Database.Model;
namespace Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistenceDbContext))]
[Migration("20241127123045_TechMessageMigration")]
[Migration("20241128074729_TechMessageMigration")]
partial class TechMessageMigration
{
/// <inheritdoc />
@ -55,27 +55,27 @@ namespace Persistence.Database.Postgres.Migrations
.HasColumnType("uuid")
.HasComment("Id события");
b.Property<int>("CategoryId")
.HasColumnType("integer")
.HasComment("Id Категории важности");
b.Property<double?>("Depth")
.HasColumnType("double precision")
.HasComment("Глубина забоя");
b.Property<int>("ImportantId")
.HasColumnType("integer")
.HasComment("Id Категории важности");
b.Property<string>("MessageText")
.IsRequired()
.HasColumnType("varchar(512)")
.HasComment("Текст сообщения");
b.Property<DateTimeOffset>("OccurrenceDate")
.HasColumnType("timestamp with time zone")
.HasComment("Дата возникновения");
b.Property<Guid>("SystemId")
.HasColumnType("uuid")
.HasComment("Id системы автобурения, к которой относится сообщение");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата возникновения");
b.Property<Guid>("UserId")
.HasColumnType("uuid")
.HasComment("Id пользователя за пультом бурильщика");

View File

@ -24,27 +24,13 @@ namespace Persistence.Database.Postgres.Migrations
table.PrimaryKey("PK_ADSystem", x => x.SystemId);
});
migrationBuilder.CreateTable(
name: "TimestampedSets",
columns: table => new
{
IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"),
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Отметка времени, строго в UTC"),
Set = table.Column<string>(type: "jsonb", nullable: false, comment: "Набор сохраняемых данных")
},
constraints: table =>
{
table.PrimaryKey("PK_TimestampedSets", x => new { x.IdDiscriminator, x.Timestamp });
},
comment: "Общая таблица данных временных рядов");
migrationBuilder.CreateTable(
name: "TechMessage",
columns: table => new
{
EventId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id события"),
ImportantId = table.Column<int>(type: "integer", nullable: false, comment: "Id Категории важности"),
OccurrenceDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"),
CategoryId = table.Column<int>(type: "integer", nullable: false, comment: "Id Категории важности"),
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"),
Depth = table.Column<double>(type: "double precision", nullable: true, comment: "Глубина забоя"),
MessageText = table.Column<string>(type: "varchar(512)", nullable: false, comment: "Текст сообщения"),
SystemId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id системы автобурения, к которой относится сообщение"),
@ -73,9 +59,6 @@ namespace Persistence.Database.Postgres.Migrations
migrationBuilder.DropTable(
name: "TechMessage");
migrationBuilder.DropTable(
name: "TimestampedSets");
migrationBuilder.DropTable(
name: "ADSystem");
}

View File

@ -52,27 +52,27 @@ namespace Persistence.Database.Postgres.Migrations
.HasColumnType("uuid")
.HasComment("Id события");
b.Property<int>("CategoryId")
.HasColumnType("integer")
.HasComment("Id Категории важности");
b.Property<double?>("Depth")
.HasColumnType("double precision")
.HasComment("Глубина забоя");
b.Property<int>("ImportantId")
.HasColumnType("integer")
.HasComment("Id Категории важности");
b.Property<string>("MessageText")
.IsRequired()
.HasColumnType("varchar(512)")
.HasComment("Текст сообщения");
b.Property<DateTimeOffset>("OccurrenceDate")
.HasColumnType("timestamp with time zone")
.HasComment("Дата возникновения");
b.Property<Guid>("SystemId")
.HasColumnType("uuid")
.HasComment("Id системы автобурения, к которой относится сообщение");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата возникновения");
b.Property<Guid>("UserId")
.HasColumnType("uuid")
.HasComment("Id пользователя за пультом бурильщика");

View File

@ -12,7 +12,7 @@ public partial class PersistenceDbContext : DbContext
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>();
public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>();
public PersistenceDbContext()
: base()
@ -42,5 +42,14 @@ public partial class PersistenceDbContext : DbContext
modelBuilder.Entity<TimestampedSet>()
.Property(e => e.Set)
.HasJsonConversion();
}
modelBuilder.Entity<TechMessage>(entity =>
{
entity.HasOne(t => t.System)
.WithMany()
.HasForeignKey(t => t.SystemId)
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
}
}

View File

@ -10,10 +10,10 @@ namespace Persistence.Database.Entity
public Guid EventId { get; set; }
[Comment("Id Категории важности")]
public int ImportantId { get; set; }
public int CategoryId { get; set; }
[Comment("Дата возникновения")]
public DateTimeOffset OccurrenceDate { get; set; }
public DateTimeOffset Timestamp { get; set; }
[Comment("Глубина забоя")]
public double? Depth { get; set; }

View File

@ -1,4 +1,5 @@
using System.Net;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Persistence.Client;
using Persistence.Client.Clients;
@ -10,7 +11,9 @@ namespace Persistence.IntegrationTests.Controllers
{
public class TechMessagesControllerTest : BaseIntegrationTest
{
private static readonly string SystemCacheKey = $"{typeof(ADSystem).FullName}CacheKey";
private readonly ITechMessagesClient techMessagesClient;
private readonly IMemoryCache memoryCache;
public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory)
{
var scope = factory.Services.CreateScope();
@ -18,6 +21,7 @@ namespace Persistence.IntegrationTests.Controllers
.GetRequiredService<PersistenceClientFactory>();
techMessagesClient = persistenceClientFactory.GetClient<ITechMessagesClient>();
memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
}
[Fact]
@ -29,7 +33,7 @@ namespace Persistence.IntegrationTests.Controllers
{
Skip = 1,
Take = 2,
SortSettings = nameof(TechMessageDto.CategoryId)
SortSettings = nameof(TechMessage.CategoryId)
};
//act
@ -53,7 +57,7 @@ namespace Persistence.IntegrationTests.Controllers
{
Skip = 0,
Take = 2,
SortSettings = nameof(TechMessageDto.CategoryId)
SortSettings = nameof(TechMessage.CategoryId)
};
//act
@ -71,11 +75,39 @@ namespace Persistence.IntegrationTests.Controllers
await InsertRange();
}
[Fact]
public async Task InsertRange_returns_BadRequest()
{
//arrange
var dtos = new List<TechMessageDto>()
{
new TechMessageDto()
{
EventId = Guid.NewGuid(),
CategoryId = -1, // < 0
Timestamp = DateTimeOffset.UtcNow,
Depth = -1, // < 0
MessageText = string.Empty, // length < 0
System = string.Concat(Enumerable.Repeat(nameof(TechMessageDto.System), 100)), // length > 256
UserId = Guid.NewGuid()
}
};
//act
var response = await techMessagesClient.InsertRange(dtos, new CancellationToken());
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task GetSystems_returns_success()
{
//arrange
dbContext.CleanupDbSet<ADSystem>();
memoryCache.Remove(SystemCacheKey);
//act
dbContext.CleanupDbSet<TechMessage>();
var response = await techMessagesClient.GetSystems(new CancellationToken());
//assert
@ -113,7 +145,7 @@ namespace Persistence.IntegrationTests.Controllers
var autoDrillingSystem = nameof(TechMessageDto.System);
//act
var response = await techMessagesClient.GetStatistics(imortantId, autoDrillingSystem, new CancellationToken());
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken());
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -124,13 +156,13 @@ namespace Persistence.IntegrationTests.Controllers
public async Task GetStatistics_AfterSave_returns_success()
{
//arrange
var imortantId = 1;
var imortantId = 0;
var autoDrillingSystem = nameof(TechMessageDto.System);
var dtos = await InsertRange();
var filteredDtos = dtos.Where(e => e.CategoryId == imortantId && e.System == e.System);
//act
var response = await techMessagesClient.GetStatistics(imortantId, autoDrillingSystem, new CancellationToken());
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken());
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

View File

@ -1,7 +1,6 @@
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json.Linq;
using Persistence.Database.Entity;
using Persistence.Models;
using Persistence.Repositories;
@ -21,7 +20,8 @@ namespace Persistence.Repository.Repositories
this.db = db;
}
protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>();
protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>()
.Include(e => e.System);
public async Task<PaginationContainer<TechMessageDto>> GetPage(RequestDto request, CancellationToken token)
{
@ -46,7 +46,7 @@ namespace Persistence.Repository.Repositories
{
var query = GetQueryReadOnly();
var count = await query
.Where(e => importantId == null || e.ImportantId == importantId)
.Where(e => importantId == null || e.CategoryId == importantId)
.Where(e => autoDrillingSystem == null || e.System.Name == autoDrillingSystem)
.GroupBy(e => e.System.Name)
.ToDictionaryAsync(e => e.Key, v => v.Count());
@ -54,7 +54,37 @@ namespace Persistence.Repository.Repositories
return count;
}
public async Task<IEnumerable<ADSystemDto>> GetSystems(CancellationToken token)
public async Task<IEnumerable<string>> GetSystems(CancellationToken token)
{
var entities = await GetSystems();
var systems = entities.Select(e => e.Name);
return systems ?? [];
}
public async Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token)
{
var entities = new List<TechMessage>();
foreach (var dto in dtos)
{
var entity = dto.Adapt<TechMessage>();
var systems = await GetSystems();
var systemId = systems.FirstOrDefault(e => e.Name == dto.System)?.SystemId
?? await CreateSystem(dto.System);
entity.SystemId = systemId;
entities.Add(entity);
}
await db.Set<TechMessage>().AddRangeAsync(entities, token);
var result = await db.SaveChangesAsync(token);
return result;
}
private async Task<IEnumerable<ADSystemDto>> GetSystems()
{
var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async f =>
{
@ -69,33 +99,6 @@ namespace Persistence.Repository.Repositories
return systems ?? [];
}
public async Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token)
{
var entities = dtos.Select(dto =>
{
var task = Task.Run(async () =>
{
var entity = dto.Adapt<TechMessage>();
var systems = await GetSystems(token);
var systemId = systems.FirstOrDefault(e => e.Name == dto.System)?.SystemId
?? await CreateSystem(dto.System);
entity.SystemId = systemId;
return entity;
});
task.Wait();
return task.Result;
});
await db.Set<TechMessage>().AddRangeAsync(entities, token);
var result = await db.SaveChangesAsync(token);
return result;
}
private async Task<Guid> CreateSystem(string name)
{
memoryCache.Remove(SystemCacheKey);
@ -110,7 +113,7 @@ namespace Persistence.Repository.Repositories
await db.Set<ADSystem>().AddAsync(entity);
await db.SaveChangesAsync();
return Guid.NewGuid();
return systemId;
}
}
}

View File

@ -29,7 +29,7 @@ namespace Persistence.Repositories
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<ADSystemDto>> GetSystems(CancellationToken token);
Task<IEnumerable<string>> GetSystems(CancellationToken token);
/// <summary>
/// Получение количества сообщений по категориям и системам автобурения