ProcessMapPlanDrilling.http - fix encoding.

Handle foreignKeys exceptions as validation exceptions.
Add integr.tests for bad request cases.
This commit is contained in:
ngfrolov 2024-01-22 11:49:45 +05:00
parent 8a38777db3
commit 54ef71411a
Signed by untrusted user who does not match committer: ng.frolov
GPG Key ID: E99907A0357B29A7
3 changed files with 127 additions and 69 deletions

View File

@ -5,8 +5,10 @@ using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudDb.Model.ProcessMapPlan;
using AsbCloudDb.Model.WellSections;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Linq;
@ -47,8 +49,8 @@ public class ProcessMapPlanBaseRepository<TDto, TEntity> : IProcessMapPlanBaseRe
entity.Obsolete = null;
dbSet.Add(entity);
}
result += await context.SaveChangesAsync(token);
result += await SaveChangesWithExceptionHandling(token);
}
return result;
}
@ -60,28 +62,21 @@ public class ProcessMapPlanBaseRepository<TDto, TEntity> : IProcessMapPlanBaseRe
using var transaction = context.Database.BeginTransaction();
var result = 0;
try
var dbSet = context.Set<TEntity>();
var entitiesToMarkDeleted = dbSet
.Where(e => e.IdWell == idWell)
.Where(e => e.Obsolete == null);
var obsolete = DateTimeOffset.UtcNow;
foreach (var entity in entitiesToMarkDeleted)
{
var dbSet = context.Set<TEntity>();
var entitiesToMarkDeleted = dbSet
.Where(e => e.IdWell == idWell)
.Where(e => e.Obsolete == null);
var obsolete = DateTimeOffset.UtcNow;
foreach (var entity in entitiesToMarkDeleted)
{
entity.IdState = ChangeLogAbstract.IdClearedOnImport;
entity.Obsolete = obsolete;
entity.IdEditor = idUser;
}
result += await context.SaveChangesAsync(token);
result += await InsertRange(idUser, dtos, token);
await transaction.CommitAsync(token);
}
catch
{
await transaction.RollbackAsync(CancellationToken.None);
throw;
entity.IdState = ChangeLogAbstract.IdClearedOnImport;
entity.Obsolete = obsolete;
entity.IdEditor = idUser;
}
result += await SaveChangesWithExceptionHandling(token);
result += await InsertRange(idUser, dtos, token);
await transaction.CommitAsync(token);
return result;
}
@ -99,7 +94,7 @@ public class ProcessMapPlanBaseRepository<TDto, TEntity> : IProcessMapPlanBaseRe
entity.Obsolete = obsolete;
entity.IdEditor = idUser;
}
var result = await context.SaveChangesAsync(token);
var result = await SaveChangesWithExceptionHandling(token);
return result;
}
@ -201,48 +196,39 @@ public class ProcessMapPlanBaseRepository<TDto, TEntity> : IProcessMapPlanBaseRe
using var transaction = context.Database.BeginTransaction();
var result = 0;
try
var ids = dtos.Select(d => d.Id);
var dbSet = context.Set<TEntity>();
var entitiesToDelete = dbSet
.Where(e => ids.Contains(e.Id));
var updateTime = DateTimeOffset.UtcNow;
foreach (var entity in entitiesToDelete)
{
var ids = dtos.Select(d => d.Id);
var dbSet = context.Set<TEntity>();
var entitiesToDelete = dbSet
.Where(e => ids.Contains(e.Id));
var updateTime = DateTimeOffset.UtcNow;
foreach (var entity in entitiesToDelete)
{
if(entity.Obsolete is not null)
throw new ArgumentInvalidException(nameof(dtos), "Недопустимо редактировать устаревшие записи");
entity.IdState = ChangeLogAbstract.IdStateReplaced;
entity.Obsolete = updateTime;
entity.IdEditor = idUser;
}
result += await context.SaveChangesAsync(token);
var entitiesNew = dtos.Select(Convert);
foreach (var entity in entitiesNew)
{
entity.IdPrevious = entity.Id;
entity.Id = default;
entity.Creation = updateTime;
entity.IdAuthor = idUser;
entity.Obsolete = null;
entity.IdEditor = null;
entity.IdState = ChangeLogAbstract.IdStateActual;
dbSet.Add(entity);
}
result += await context.SaveChangesAsync(token);
await transaction.CommitAsync(token);
if(entity.Obsolete is not null)
throw new ArgumentInvalidException(nameof(dtos), "Недопустимо редактировать устаревшие записи");
entity.IdState = ChangeLogAbstract.IdStateReplaced;
entity.Obsolete = updateTime;
entity.IdEditor = idUser;
}
catch
result += await context.SaveChangesAsync(token);
var entitiesNew = dtos.Select(Convert);
foreach (var entity in entitiesNew)
{
await transaction.RollbackAsync(CancellationToken.None);
throw;
entity.IdPrevious = entity.Id;
entity.Id = default;
entity.Creation = updateTime;
entity.IdAuthor = idUser;
entity.Obsolete = null;
entity.IdEditor = null;
entity.IdState = ChangeLogAbstract.IdStateActual;
dbSet.Add(entity);
}
result += await SaveChangesWithExceptionHandling(token);
await transaction.CommitAsync(token);
return result;
}
@ -267,4 +253,25 @@ public class ProcessMapPlanBaseRepository<TDto, TEntity> : IProcessMapPlanBaseRe
return dto;
}
private async Task<int> SaveChangesWithExceptionHandling(CancellationToken token)
{
try
{
var result = await context.SaveChangesAsync(token);
return result;
}
catch (DbUpdateException ex)
{
if (ex.InnerException is PostgresException pgException)
TryConvertPostgresExceptionToValidateException(pgException);
throw;
}
}
private static void TryConvertPostgresExceptionToValidateException(PostgresException pgException)
{
if (pgException.SqlState == PostgresErrorCodes.ForeignKeyViolation)
throw new ArgumentInvalidException("dtos", pgException.Message + "\r\n" + pgException.Detail);
}
}

View File

@ -117,6 +117,57 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest
MatchHelper.Match(expected, actual, excludeProps);
}
[Fact]
public async Task InsertRange_returns_BadRequest_for_IdWellSectionType()
{
//arrange
var dbset = dbContext.Set<ProcessMapPlanDrilling>();
dbset.RemoveRange(dbset);
dbContext.SaveChanges();
var badDto = dto.Adapt<ProcessMapPlanDrillingDto>();
badDto.IdWellSectionType = int.MaxValue;
//act
var response = await client.InsertRange(dto.IdWell, new[] { badDto });
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task InsertRange_returns_BadRequest_for_IdMode()
{
//arrange
var dbset = dbContext.Set<ProcessMapPlanDrilling>();
dbset.RemoveRange(dbset);
dbContext.SaveChanges();
var badDto = dto.Adapt<ProcessMapPlanDrillingDto>();
badDto.IdMode = int.MaxValue;
//act
var response = await client.InsertRange(dto.IdWell, new[] { badDto });
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task InsertRange_returns_BadRequest_for_IdWell()
{
//arrange
var dbset = dbContext.Set<ProcessMapPlanDrilling>();
dbset.RemoveRange(dbset);
dbContext.SaveChanges();
var badDto = dto.Adapt<ProcessMapPlanDrillingDto>();
badDto.IdWell = int.MaxValue;
//act
var response = await client.InsertRange(dto.IdWell, new[] { badDto });
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task ClearAndInsertRange_returns_success()
{

View File

@ -7,7 +7,7 @@
@id = 1
@idWell = 1
### получение данных drill test с панели и сохранение их в ЕЦП
### получение данных drill test с панели и сохранение их в ЕЦП
POST {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling
Content-Type: {{contentType}}
accept: */*
@ -54,12 +54,12 @@ Authorization: {{auth}}
}
]
### Получение всех
### Получение всех
GET {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling
accept: */*
Authorization: {{auth}}
### замена старых записей новыми
### замена старых записей новыми
POST {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling/replace
Content-Type: {{contentType}}
accept: */*
@ -75,7 +75,7 @@ Authorization: {{auth}}
"idState": 0,
"idPrevious": 0,
"idWell": 1,
"idWellSectionType": 1,
"idWellSectionType": 1500,
"depthStart": 0,
"depthEnd": 10,
"idMode": 2,
@ -106,22 +106,22 @@ Authorization: {{auth}}
}
]
### Получение только актуальных
### Получение только актуальных
GET {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling?Moment=3000-01-01
accept: */*
Authorization: {{auth}}
### Получение
### Получение
GET {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling/dates
accept: */*
Authorization: {{auth}}
### Получение изменений за дату
### Получение изменений за дату
GET {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling/changeLog?date=2024-01-19
accept: */*
Authorization: {{auth}}
### удаление
### удаление
DELETE {{baseUrl}}/api/well/{{idWell}}/ProcessMapPlanDrilling
Content-Type: {{contentType}}
accept: */*