diff --git a/Persistence.API/Controllers/ChangeLogController.cs b/Persistence.API/Controllers/ChangeLogController.cs index 233af58..54a3b0c 100644 --- a/Persistence.API/Controllers/ChangeLogController.cs +++ b/Persistence.API/Controllers/ChangeLogController.cs @@ -19,10 +19,10 @@ public class ChangeLogController : ControllerBase, IChangeLogApi this.repository = repository; } - [HttpPost] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] + [HttpPost("{idDiscriminator}")] + [ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)] public async Task Add( - Guid idDiscriminator, + [FromRoute] Guid idDiscriminator, [FromBody] DataWithWellDepthAndSectionDto dto, CancellationToken token) { @@ -32,11 +32,11 @@ public class ChangeLogController : ControllerBase, IChangeLogApi return CreatedAtAction(nameof(Add), result); } - [HttpPost("range")] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] + [HttpPost("range/{idDiscriminator}")] + [ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)] public async Task AddRange( - Guid idDiscriminator, - [FromBody] IEnumerable dtos, + [FromRoute] Guid idDiscriminator, + [FromBody] IEnumerable dtos, CancellationToken token = default) { var userId = User.GetUserId(); @@ -65,11 +65,11 @@ public class ChangeLogController : ControllerBase, IChangeLogApi return Ok(result); } - [HttpPost("replace")] + [HttpPost("replace/{idDiscriminator}")] [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] public async Task ClearAndAddRange( - Guid idDiscriminator, - IEnumerable dtos, + [FromRoute] Guid idDiscriminator, + [FromBody] IEnumerable dtos, CancellationToken token = default) { var userId = User.GetUserId(); @@ -101,11 +101,11 @@ public class ChangeLogController : ControllerBase, IChangeLogApi return Ok(result); } - [HttpGet] + [HttpGet("{idDiscriminator}")] [ProducesResponseType(typeof(PaginationContainer), (int)HttpStatusCode.OK)] public async Task GetCurrent( - Guid idDiscriminator, - [FromQuery]SectionPartRequest filterRequest, + [FromRoute] Guid idDiscriminator, + [FromQuery] SectionPartRequest filterRequest, [FromQuery] PaginationRequest paginationRequest, CancellationToken token = default) { @@ -115,10 +115,10 @@ public class ChangeLogController : ControllerBase, IChangeLogApi return Ok(result); } - [HttpGet("moment")] + [HttpGet("moment/{idDiscriminator}")] [ProducesResponseType(typeof(PaginationContainer), (int)HttpStatusCode.OK)] public async Task GetByDate( - Guid idDiscriminator, + [FromRoute] Guid idDiscriminator, DateTimeOffset moment, [FromQuery] SectionPartRequest filterRequest, [FromQuery] PaginationRequest paginationRequest, @@ -129,11 +129,11 @@ public class ChangeLogController : ControllerBase, IChangeLogApi return Ok(result); } - [HttpGet("history")] + [HttpGet("history/{idDiscriminator}")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] public async Task GetChangeLogForDate( - Guid idDiscriminator, - DateTimeOffset dateBegin, + [FromRoute] Guid idDiscriminator, + DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token = default) { @@ -142,31 +142,32 @@ public class ChangeLogController : ControllerBase, IChangeLogApi return Ok(result); } - [HttpGet("datesChange")] + [HttpGet("datesChange/{idDiscriminator}")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task GetDatesChange(Guid idDiscriminator, CancellationToken token = default) + public async Task GetDatesChange([FromRoute] Guid idDiscriminator, CancellationToken token = default) { var result = await repository.GetDatesChange(idDiscriminator, token); return Ok(result); } - [HttpGet("part")] + [HttpGet("part/{idDiscriminator}")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task GetPart(Guid idDiscriminator, DateTimeOffset dateBegin, int take = 86400, CancellationToken token = default) + public async Task GetPart([FromRoute] Guid idDiscriminator, DateTimeOffset dateBegin, int take = 86400, CancellationToken token = default) { var result = await repository.GetGtDate(idDiscriminator, dateBegin, token); return Ok(result); } - [HttpGet("datesRange")] + [HttpGet("datesRange/{idDiscriminator}")] [ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)] - public async Task GetDatesRangeAsync(Guid idDiscriminator, CancellationToken token = default) + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public async Task GetDatesRangeAsync([FromRoute] Guid idDiscriminator, CancellationToken token = default) { var result = await repository.GetDatesRange(idDiscriminator, token); - if(result is null) + if (result is null) return NoContent(); return Ok(result); diff --git a/Persistence.Database/Entity/ChangeLog.cs b/Persistence.Database/Entity/ChangeLog.cs index 7100fc1..2585ace 100644 --- a/Persistence.Database/Entity/ChangeLog.cs +++ b/Persistence.Database/Entity/ChangeLog.cs @@ -1,6 +1,7 @@  using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; namespace Persistence.Database.Model; @@ -9,69 +10,36 @@ namespace Persistence.Database.Model; /// public class ChangeLog : IChangeLog { - /// - /// Ключ записи - /// - [Key, Column("Id")] + [Key, Comment("Ключ записи")] public Guid Id { get; set; } - /// - /// Дискриминатор таблицы - /// - [Column("IdDiscriminator")] + [Comment("Дискриминатор таблицы")] public Guid IdDiscriminator { get; set; } - /// - /// Автор изменения - /// - [Column("IdAuthor")] + [Comment("Автор изменения")] public Guid IdAuthor { get; set; } - /// - /// Редактор - /// - [Column("IdEditor")] + [Comment("Редактор")] public Guid? IdEditor { get; set; } - /// - /// Дата создания записи - /// - [Column("Creation")] + [Comment("Дата создания записи")] public DateTimeOffset Creation { get; set; } - /// - /// Дата устаревания (например при удалении) - /// - [Column("Obsolete")] + [Comment("Дата устаревания (например при удалении)")] public DateTimeOffset? Obsolete { get; set; } - /// - /// Id заменяющей записи - /// - [Column("IdNext")] + [Comment("Id заменяющей записи")] public Guid? IdNext { get; set; } - /// - /// Глубина забоя на дату начала интервала - /// - [Column("DepthStart")] + [Comment("Глубина забоя на дату начала интервала")] public double DepthStart { get; set; } - /// - /// Глубина забоя на дату окончания интервала - /// - [Column("DepthEnd")] + [Comment("Глубина забоя на дату окончания интервала")] public double DepthEnd { get; set; } - /// - /// Ключ секции - /// - [Column("IdSection")] + [Comment("Ключ секции")] public Guid IdSection { get; set; } - /// - /// Значение - /// - [Column("Value", TypeName = "jsonb")] + [Column(TypeName = "jsonb"), Comment("Значение")] public required IDictionary Value { get; set; } } diff --git a/Persistence.Database/Entity/ITimestampedData.cs b/Persistence.Database/Entity/ITimestampedData.cs index 9241ca5..ce21da5 100644 --- a/Persistence.Database/Entity/ITimestampedData.cs +++ b/Persistence.Database/Entity/ITimestampedData.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Persistence.Database.Model; +namespace Persistence.Database.Model; public interface ITimestampedData { /// diff --git a/Persistence.IntegrationTests/Controllers/ChangeLogControllerTest.cs b/Persistence.IntegrationTests/Controllers/ChangeLogControllerTest.cs index 0f24bb8..8fc572a 100644 --- a/Persistence.IntegrationTests/Controllers/ChangeLogControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/ChangeLogControllerTest.cs @@ -24,7 +24,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest } [Fact] - public async Task ClearAndInsertRange() + public async Task ClearAndInsertRange_InEmptyDb() { // arrange var idDiscriminator = Guid.NewGuid(); @@ -38,6 +38,23 @@ public class ChangeLogControllerTest : BaseIntegrationTest Assert.Equal(2, result.Content); } + [Fact] + public async Task ClearAndInsertRange_InNotEmptyDb() + { + // arrange + var insertedCount = 10; + var createdResult = CreateChangeLogItems(insertedCount, (-15, 15)); + var idDiscriminator = createdResult.Item1; + var dtos = createdResult.Item2.Select(e => e.Adapt()); + + // act + var result = await client.ClearAndAddRange(idDiscriminator, dtos); + + // assert + Assert.Equal(HttpStatusCode.OK, result.StatusCode); + Assert.Equal(insertedCount*2, result.Content); + } + [Fact] public async Task Add_returns_success() { diff --git a/Persistence.Repository/Persistence.Repository.csproj b/Persistence.Repository/Persistence.Repository.csproj index 55bd8ea..833fc6f 100644 --- a/Persistence.Repository/Persistence.Repository.csproj +++ b/Persistence.Repository/Persistence.Repository.csproj @@ -8,6 +8,7 @@ + diff --git a/Persistence.Repository/Repositories/ChangeLogRepository.cs b/Persistence.Repository/Repositories/ChangeLogRepository.cs index 07159d7..16790a7 100644 --- a/Persistence.Repository/Repositories/ChangeLogRepository.cs +++ b/Persistence.Repository/Repositories/ChangeLogRepository.cs @@ -4,6 +4,7 @@ using Persistence.Database.Model; using Persistence.Models; using Persistence.Models.Requests; using Persistence.Repositories; +using UuidExtensions; namespace Persistence.Repository.Repositories; public class ChangeLogRepository : IChangeLogRepository @@ -17,11 +18,13 @@ public class ChangeLogRepository : IChangeLogRepository public async Task AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable dtos, CancellationToken token) { + var entities = new List(); foreach (var dto in dtos) { var entity = CreateEntityFromDto(idAuthor, idDiscriminator, dto); - db.Set().Add(entity); + entities.Add(entity); } + db.Set().AddRange(entities); var result = await db.SaveChangesAsync(token); @@ -30,10 +33,18 @@ public class ChangeLogRepository : IChangeLogRepository public async Task MarkAsDeleted(Guid idEditor, IEnumerable ids, CancellationToken token) { - var query = db.Set().Where(s => ids.Contains(s.Id)); + var query = db.Set() + .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 Clear(idEditor, entities, token); + var result = await MarkAsObsolete(idEditor, entities, token); return result; } @@ -43,14 +54,15 @@ public class ChangeLogRepository : IChangeLogRepository var query = db.Set() .Where(s => s.IdDiscriminator == idDiscriminator) .Where(e => e.Obsolete == null); + var entities = await query.ToArrayAsync(token); - var result = await Clear(idEditor, entities, token); + var result = await MarkAsObsolete(idEditor, entities, token); return result; } - private async Task Clear(Guid idEditor, IEnumerable entities, CancellationToken token) + private async Task MarkAsObsolete(Guid idEditor, IEnumerable entities, CancellationToken token) { var updateTime = DateTimeOffset.UtcNow; @@ -66,20 +78,16 @@ public class ChangeLogRepository : IChangeLogRepository public async Task ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable dtos, CancellationToken token) { var result = 0; - using var transaction = await db.Database.BeginTransactionAsync(token); - try - { - result += await MarkAsDeleted(idAuthor, idDiscriminator, token); - result += await AddRange(idAuthor, idDiscriminator, dtos, token); - await transaction.CommitAsync(token); - return result; - } - catch - { - await transaction.RollbackAsync(token); - throw; - } + 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 UpdateRange(Guid idEditor, IEnumerable dtos, CancellationToken token) @@ -100,7 +108,7 @@ public class ChangeLogRepository : IChangeLogRepository var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id); if(updatedEntity is null) { - throw new ArgumentNullException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto)); + throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto)); } var newEntity = CreateEntityFromDto(idEditor, updatedEntity.IdDiscriminator, dto); @@ -131,6 +139,7 @@ public class ChangeLogRepository : IChangeLogRepository CancellationToken token) { var query = BuildQuery(idDiscriminator, momentUtc, filterRequest); + //ApplyFilter(query, , filterRequest); var result = await BuildPaginationContainer(query, paginationRequest, token); return result; @@ -138,6 +147,7 @@ public class ChangeLogRepository : IChangeLogRepository private IQueryable BuildQuery(Guid idDiscriminator, DateTimeOffset momentUtc, SectionPartRequest request) { + momentUtc = momentUtc.ToUniversalTime(); var query = db.Set() .Where(e => e.IdDiscriminator == idDiscriminator) .Where(e => e.Creation <= momentUtc) @@ -242,7 +252,7 @@ public class ChangeLogRepository : IChangeLogRepository { var entity = new ChangeLog() { - Id = default, + Id = Uuid7.Guid(), Creation = DateTimeOffset.UtcNow, IdAuthor = idAuthor, IdDiscriminator = idDiscriminator,