Merge branch 'master' into WitsData
This commit is contained in:
commit
cdd7aee8e9
178
Persistence.API/Controllers/ChangeLogController.cs
Normal file
178
Persistence.API/Controllers/ChangeLogController.cs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
|
using Persistence.Repositories;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Persistence.API.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class ChangeLogController : ControllerBase, IChangeLogApi
|
||||||
|
{
|
||||||
|
private IChangeLogRepository repository;
|
||||||
|
|
||||||
|
public ChangeLogController(IChangeLogRepository repository)
|
||||||
|
{
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
||||||
|
public async Task<IActionResult> Add(
|
||||||
|
[FromRoute] Guid idDiscriminator,
|
||||||
|
[FromBody] DataWithWellDepthAndSectionDto dto,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.AddRange(userId, idDiscriminator, [dto], token);
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(Add), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("range/{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
||||||
|
public async Task<IActionResult> AddRange(
|
||||||
|
[FromRoute] Guid idDiscriminator,
|
||||||
|
[FromBody] IEnumerable<DataWithWellDepthAndSectionDto> dtos,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.AddRange(userId, idDiscriminator, dtos, token);
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(AddRange), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> Delete(Guid id, CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.MarkAsDeleted(userId, [id], token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("range")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> DeleteRange(IEnumerable<Guid> ids, CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.MarkAsDeleted(userId, ids, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("replace/{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> ClearAndAddRange(
|
||||||
|
[FromRoute] Guid idDiscriminator,
|
||||||
|
[FromBody] IEnumerable<DataWithWellDepthAndSectionDto> dtos,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.ClearAndAddRange(userId, idDiscriminator, dtos, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> Update(
|
||||||
|
DataWithWellDepthAndSectionDto dto,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.UpdateRange(userId, [dto], token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("range")]
|
||||||
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> UpdateRange(
|
||||||
|
IEnumerable<DataWithWellDepthAndSectionDto> dtos,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId<Guid>();
|
||||||
|
var result = await repository.UpdateRange(userId, dtos, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(PaginationContainer<DataWithWellDepthAndSectionDto>), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> GetCurrent(
|
||||||
|
[FromRoute] Guid idDiscriminator,
|
||||||
|
[FromQuery] SectionPartRequest filterRequest,
|
||||||
|
[FromQuery] PaginationRequest paginationRequest,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var moment = new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||||
|
var result = await repository.GetByDate(idDiscriminator, moment, filterRequest, paginationRequest, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("moment/{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(PaginationContainer<DataWithWellDepthAndSectionDto>), (int)HttpStatusCode.OK)]
|
||||||
|
public async Task<IActionResult> GetByDate(
|
||||||
|
[FromRoute] Guid idDiscriminator,
|
||||||
|
DateTimeOffset moment,
|
||||||
|
[FromQuery] SectionPartRequest filterRequest,
|
||||||
|
[FromQuery] PaginationRequest paginationRequest,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.GetByDate(idDiscriminator, moment, filterRequest, paginationRequest, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("history/{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<ChangeLogDto>), (int)HttpStatusCode.OK)]
|
||||||
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
|
public async Task<IActionResult> GetChangeLogForDate(
|
||||||
|
[FromRoute] Guid idDiscriminator,
|
||||||
|
DateTimeOffset dateBegin,
|
||||||
|
DateTimeOffset dateEnd,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("datesChange/{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<DateOnly>), (int)HttpStatusCode.OK)]
|
||||||
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
|
public async Task<IActionResult> GetDatesChange([FromRoute] Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.GetDatesChange(idDiscriminator, token);
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("part/{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<DataWithWellDepthAndSectionDto>), (int)HttpStatusCode.OK)]
|
||||||
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
|
public async Task<IActionResult> 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/{idDiscriminator}")]
|
||||||
|
[ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
|
||||||
|
[ProducesResponseType((int)HttpStatusCode.NoContent)]
|
||||||
|
public async Task<IActionResult> GetDatesRangeAsync([FromRoute] Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = await repository.GetDatesRange(idDiscriminator, token);
|
||||||
|
|
||||||
|
if (result is null)
|
||||||
|
return NoContent();
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
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.Repositories;
|
using Persistence.Repositories;
|
||||||
|
|
||||||
namespace Persistence.API.Controllers;
|
namespace Persistence.API.Controllers;
|
||||||
@ -36,7 +37,7 @@ public class TechMessagesController : ControllerBase
|
|||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<PaginationContainer<TechMessageDto>>> GetPage([FromQuery] RequestDto 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);
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Text.Json.Nodes;
|
|
||||||
using Mapster;
|
using Mapster;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
@ -11,17 +9,19 @@ using Persistence.Models.Configurations;
|
|||||||
using Persistence.Services;
|
using Persistence.Services;
|
||||||
using Persistence.Services.Interfaces;
|
using Persistence.Services.Interfaces;
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
namespace Persistence.API;
|
namespace Persistence.API;
|
||||||
|
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
{
|
{
|
||||||
public static void MapsterSetup()
|
public static void MapsterSetup()
|
||||||
{
|
{
|
||||||
TypeAdapterConfig.GlobalSettings.Default.Config
|
TypeAdapterConfig.GlobalSettings.Default.Config
|
||||||
.ForType<TechMessageDto, TechMessage>()
|
.ForType<TechMessageDto, TechMessage>()
|
||||||
.Ignore(dest => dest.System, dest => dest.SystemId);
|
.Ignore(dest => dest.System, dest => dest.SystemId);
|
||||||
}
|
}
|
||||||
public static void AddSwagger(this IServiceCollection services, IConfiguration configuration)
|
public static void AddSwagger(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddSwaggerGen(c =>
|
services.AddSwaggerGen(c =>
|
||||||
@ -45,16 +45,16 @@ public static class DependencyInjection
|
|||||||
|
|
||||||
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Persistence web api", Version = "v1" });
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Persistence web api", Version = "v1" });
|
||||||
|
|
||||||
var needUseKeyCloak = configuration.GetSection("NeedUseKeyCloak").Get<bool>();
|
var needUseKeyCloak = configuration.GetSection("NeedUseKeyCloak").Get<bool>();
|
||||||
if (needUseKeyCloak)
|
if (needUseKeyCloak)
|
||||||
c.AddKeycloackSecurity(configuration);
|
c.AddKeycloackSecurity(configuration);
|
||||||
else c.AddDefaultSecurity(configuration);
|
else c.AddDefaultSecurity(configuration);
|
||||||
|
|
||||||
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);
|
||||||
var includeControllerXmlComment = true;
|
var includeControllerXmlComment = true;
|
||||||
c.IncludeXmlComments(xmlPath, includeControllerXmlComment);
|
c.IncludeXmlComments(xmlPath, includeControllerXmlComment);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddServices(this IServiceCollection services)
|
public static void AddServices(this IServiceCollection services)
|
||||||
@ -66,146 +66,146 @@ public static class DependencyInjection
|
|||||||
public static void AddJWTAuthentication(this IServiceCollection services, IConfiguration configuration)
|
public static void AddJWTAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var needUseKeyCloak = configuration
|
var needUseKeyCloak = configuration
|
||||||
.GetSection("NeedUseKeyCloak")
|
.GetSection("NeedUseKeyCloak")
|
||||||
.Get<bool>();
|
.Get<bool>();
|
||||||
if (needUseKeyCloak)
|
if (needUseKeyCloak)
|
||||||
services.AddKeyCloakAuthentication(configuration);
|
services.AddKeyCloakAuthentication(configuration);
|
||||||
else services.AddDefaultAuthentication(configuration);
|
else services.AddDefaultAuthentication(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddKeyCloakAuthentication(this IServiceCollection services, IConfiguration configuration)
|
private static void AddKeyCloakAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(options =>
|
.AddJwtBearer(options =>
|
||||||
{
|
{
|
||||||
options.RequireHttpsMetadata = false;
|
options.RequireHttpsMetadata = false;
|
||||||
options.Audience = configuration["Authentication:Audience"];
|
options.Audience = configuration["Authentication:Audience"];
|
||||||
options.MetadataAddress = configuration["Authentication:MetadataAddress"]!;
|
options.MetadataAddress = configuration["Authentication:MetadataAddress"]!;
|
||||||
options.TokenValidationParameters = new TokenValidationParameters
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
{
|
{
|
||||||
ValidIssuer = configuration["Authentication:ValidIssuer"],
|
ValidIssuer = configuration["Authentication:ValidIssuer"],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddDefaultAuthentication(this IServiceCollection services, IConfiguration configuration)
|
private static void AddDefaultAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(options =>
|
.AddJwtBearer(options =>
|
||||||
{
|
{
|
||||||
options.RequireHttpsMetadata = false;
|
options.RequireHttpsMetadata = false;
|
||||||
options.TokenValidationParameters = new TokenValidationParameters
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
{
|
{
|
||||||
ValidateIssuer = true,
|
ValidateIssuer = true,
|
||||||
ValidIssuer = JwtParams.Issuer,
|
ValidIssuer = JwtParams.Issuer,
|
||||||
ValidateAudience = true,
|
ValidateAudience = true,
|
||||||
ValidAudience = JwtParams.Audience,
|
ValidAudience = JwtParams.Audience,
|
||||||
ValidateLifetime = true,
|
ValidateLifetime = true,
|
||||||
IssuerSigningKey = JwtParams.SecurityKey,
|
IssuerSigningKey = JwtParams.SecurityKey,
|
||||||
ValidateIssuerSigningKey = false
|
ValidateIssuerSigningKey = false,
|
||||||
};
|
};
|
||||||
options.Events = new JwtBearerEvents
|
options.Events = new JwtBearerEvents
|
||||||
{
|
{
|
||||||
OnMessageReceived = context =>
|
OnMessageReceived = context =>
|
||||||
{
|
{
|
||||||
var accessToken = context.Request.Headers["Authorization"]
|
var accessToken = context.Request.Headers["Authorization"]
|
||||||
.ToString()
|
.ToString()
|
||||||
.Replace(JwtBearerDefaults.AuthenticationScheme, string.Empty)
|
.Replace(JwtBearerDefaults.AuthenticationScheme, string.Empty)
|
||||||
.Trim();
|
.Trim();
|
||||||
|
|
||||||
context.Token = accessToken;
|
context.Token = accessToken;
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
},
|
},
|
||||||
OnTokenValidated = context =>
|
OnTokenValidated = context =>
|
||||||
{
|
{
|
||||||
var username = context.Principal?.Claims
|
var username = context.Principal?.Claims
|
||||||
.FirstOrDefault(e => e.Type == "username")?.Value;
|
.FirstOrDefault(e => e.Type == "username")?.Value;
|
||||||
|
|
||||||
var password = context.Principal?.Claims
|
var password = context.Principal?.Claims
|
||||||
.FirstOrDefault(e => e.Type == "password")?.Value;
|
.FirstOrDefault(e => e.Type == "password")?.Value;
|
||||||
|
|
||||||
var keyCloakUser = configuration
|
var keyCloakUser = configuration
|
||||||
.GetSection(nameof(AuthUser))
|
.GetSection(nameof(AuthUser))
|
||||||
.Get<AuthUser>()!;
|
.Get<AuthUser>()!;
|
||||||
|
|
||||||
if (username != keyCloakUser.Username || password != keyCloakUser.Password)
|
if (username != keyCloakUser.Username || password != keyCloakUser.Password)
|
||||||
{
|
{
|
||||||
context.Fail("username or password did not match");
|
context.Fail("username or password did not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Security (Swagger)
|
#region Security (Swagger)
|
||||||
private static void AddKeycloackSecurity(this SwaggerGenOptions options, IConfiguration configuration)
|
private static void AddKeycloackSecurity(this SwaggerGenOptions options, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
options.AddSecurityDefinition("Keycloack", new OpenApiSecurityScheme
|
options.AddSecurityDefinition("Keycloack", 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 12345abcdef'",
|
||||||
Name = "Authorization",
|
Name = "Authorization",
|
||||||
In = ParameterLocation.Header,
|
In = ParameterLocation.Header,
|
||||||
Type = SecuritySchemeType.OAuth2,
|
Type = SecuritySchemeType.OAuth2,
|
||||||
Flows = new OpenApiOAuthFlows
|
Flows = new OpenApiOAuthFlows
|
||||||
{
|
{
|
||||||
Implicit = new OpenApiOAuthFlow
|
Implicit = new OpenApiOAuthFlow
|
||||||
{
|
{
|
||||||
AuthorizationUrl = new Uri(configuration["Authentication:AuthorizationUrl"]),
|
AuthorizationUrl = new Uri(configuration["Authentication:AuthorizationUrl"]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
new OpenApiSecurityScheme
|
new OpenApiSecurityScheme
|
||||||
{
|
{
|
||||||
Reference = new OpenApiReference
|
Reference = new OpenApiReference
|
||||||
{
|
{
|
||||||
Type = ReferenceType.SecurityScheme,
|
Type = ReferenceType.SecurityScheme,
|
||||||
Id = "Keycloack"
|
Id = "Keycloack"
|
||||||
},
|
},
|
||||||
Scheme = "Bearer",
|
Scheme = "Bearer",
|
||||||
Name = "Bearer",
|
Name = "Bearer",
|
||||||
In = ParameterLocation.Header,
|
In = ParameterLocation.Header,
|
||||||
},
|
},
|
||||||
new List<string>()
|
new List<string>()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddDefaultSecurity(this SwaggerGenOptions options, IConfiguration configuration)
|
private static void AddDefaultSecurity(this SwaggerGenOptions options, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
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 12345abcdef'",
|
||||||
Name = "Authorization",
|
Name = "Authorization",
|
||||||
In = ParameterLocation.Header,
|
In = ParameterLocation.Header,
|
||||||
Type = SecuritySchemeType.ApiKey,
|
Type = SecuritySchemeType.ApiKey,
|
||||||
Scheme = "Bearer",
|
Scheme = "Bearer",
|
||||||
});
|
});
|
||||||
|
|
||||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
new OpenApiSecurityScheme
|
new OpenApiSecurityScheme
|
||||||
{
|
{
|
||||||
Reference = new OpenApiReference
|
Reference = new OpenApiReference
|
||||||
{
|
{
|
||||||
Type = ReferenceType.SecurityScheme,
|
Type = ReferenceType.SecurityScheme,
|
||||||
Id = "Bearer"
|
Id = "Bearer"
|
||||||
},
|
},
|
||||||
Scheme = "oauth2",
|
Scheme = "oauth2",
|
||||||
Name = "Bearer",
|
Name = "Bearer",
|
||||||
In = ParameterLocation.Header,
|
In = ParameterLocation.Header,
|
||||||
},
|
},
|
||||||
new List<string>()
|
new List<string>()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ public class Startup
|
|||||||
services.AddSwagger(Configuration);
|
services.AddSwagger(Configuration);
|
||||||
services.AddInfrastructure();
|
services.AddInfrastructure();
|
||||||
services.AddPersistenceDbContext(Configuration);
|
services.AddPersistenceDbContext(Configuration);
|
||||||
|
services.AddAuthorization();
|
||||||
services.AddJWTAuthentication(Configuration);
|
services.AddJWTAuthentication(Configuration);
|
||||||
services.AddMemoryCache();
|
services.AddMemoryCache();
|
||||||
services.AddServices();
|
services.AddServices();
|
||||||
@ -41,6 +42,9 @@ public class Startup
|
|||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
@ -55,7 +59,7 @@ public class Startup
|
|||||||
using var scope = host.Services.CreateScope();
|
using var scope = host.Services.CreateScope();
|
||||||
var provider = scope.ServiceProvider;
|
var provider = scope.ServiceProvider;
|
||||||
|
|
||||||
var context = provider.GetRequiredService<PersistenceDbContext>();
|
var context = provider.GetRequiredService<PersistencePostgresContext>();
|
||||||
context.Database.EnsureCreatedAndMigrated();
|
context.Database.EnsureCreatedAndMigrated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,5 +14,13 @@
|
|||||||
"Audience": "account",
|
"Audience": "account",
|
||||||
"ValidIssuer": "http://192.168.0.10:8321/realms/Persistence",
|
"ValidIssuer": "http://192.168.0.10:8321/realms/Persistence",
|
||||||
"AuthorizationUrl": "http://192.168.0.10:8321/realms/Persistence/protocol/openid-connect/auth"
|
"AuthorizationUrl": "http://192.168.0.10:8321/realms/Persistence/protocol/openid-connect/auth"
|
||||||
|
},
|
||||||
|
"NeedUseKeyCloak": false,
|
||||||
|
"AuthUser": {
|
||||||
|
"username": "myuser",
|
||||||
|
"password": 12345,
|
||||||
|
"clientId": "webapi",
|
||||||
|
"grantType": "password",
|
||||||
|
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "7d9f3574-6574-4ca3-845a-0276eb4aa8f6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
Persistence.Client/Clients/IChangeLogClient.cs
Normal file
106
Persistence.Client/Clients/IChangeLogClient.cs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
|
using Refit;
|
||||||
|
|
||||||
|
namespace Persistence.Client.Clients;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Интерфейс для тестирования API, предназначенного для работы с записями ChangeLod
|
||||||
|
/// </summary>
|
||||||
|
public interface IChangeLogClient
|
||||||
|
{
|
||||||
|
private const string BaseRoute = "/api/ChangeLog";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Импорт с заменой: удаление старых строк и добавление новых
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Post($"{BaseRoute}/replace/{{idDiscriminator}}")]
|
||||||
|
Task<IApiResponse<int>> ClearAndAddRange(Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение актуальных данных на определенную дату (с пагинацией)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="moment"></param>
|
||||||
|
/// <param name="filterRequest">параметры запроса фильтрации</param>
|
||||||
|
/// <param name="paginationRequest">параметры запроса пагинации</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Get($"{BaseRoute}/moment/{{idDiscriminator}}")]
|
||||||
|
Task<IApiResponse<PaginationContainer<DataWithWellDepthAndSectionDto>>> GetByDate(
|
||||||
|
Guid idDiscriminator,
|
||||||
|
DateTimeOffset moment,
|
||||||
|
[Query] SectionPartRequest filterRequest,
|
||||||
|
[Query] PaginationRequest paginationRequest);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение исторических данных за определенный период времени
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dateBegin"></param>
|
||||||
|
/// <param name="dateEnd"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Get($"{BaseRoute}/history/{{idDiscriminator}}")]
|
||||||
|
Task<IApiResponse<IEnumerable<ChangeLogDto>>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить одну запись
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Post($"{BaseRoute}/{{idDiscriminator}}")]
|
||||||
|
Task<IApiResponse<int>> Add(Guid idDiscriminator, DataWithWellDepthAndSectionDto dto);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить несколько записей
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Post($"{BaseRoute}/range/{{idDiscriminator}}")]
|
||||||
|
Task<IApiResponse<int>> AddRange(Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обновить одну запись
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Put($"{BaseRoute}")]
|
||||||
|
Task<IApiResponse<int>> Update(DataWithWellDepthAndSectionDto dto);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обновить несколько записей
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Put($"{BaseRoute}/range")]
|
||||||
|
Task<IApiResponse<int>> UpdateRange(IEnumerable<DataWithWellDepthAndSectionDto> dtos);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Удалить одну запись
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Delete($"{BaseRoute}")]
|
||||||
|
Task<IApiResponse<int>> Delete(Guid id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Удалить несколько записей
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ids"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Delete($"{BaseRoute}/range")]
|
||||||
|
Task<IApiResponse<int>> DeleteRange([Body] IEnumerable<Guid> ids);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Get($"{BaseRoute}/datesRange/{{idDiscriminator}}")]
|
||||||
|
Task<IApiResponse<DatesRangeDto?>> GetDatesRange(Guid idDiscriminator);
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
using Refit;
|
using Refit;
|
||||||
|
|
||||||
namespace Persistence.Client.Clients
|
namespace Persistence.Client.Clients
|
||||||
@ -11,7 +12,7 @@ namespace Persistence.Client.Clients
|
|||||||
private const string BaseRoute = "/api/techMessages";
|
private const string BaseRoute = "/api/techMessages";
|
||||||
|
|
||||||
[Get($"{BaseRoute}")]
|
[Get($"{BaseRoute}")]
|
||||||
Task<IApiResponse<PaginationContainer<TechMessageDto>>> GetPage([Query] RequestDto 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);
|
||||||
|
@ -36,7 +36,8 @@ public static class ApiTokenHelper
|
|||||||
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
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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;
|
||||||
|
|
||||||
@ -10,10 +11,10 @@ public static class DependencyInjection
|
|||||||
{
|
{
|
||||||
string connectionStringName = "DefaultConnection";
|
string connectionStringName = "DefaultConnection";
|
||||||
|
|
||||||
services.AddDbContext<PersistenceDbContext>(options =>
|
services.AddDbContext<PersistencePostgresContext>(options =>
|
||||||
options.UseNpgsql(configuration.GetConnectionString(connectionStringName)));
|
options.UseNpgsql(configuration.GetConnectionString(connectionStringName)));
|
||||||
|
|
||||||
services.AddScoped<DbContext>(provider => provider.GetRequiredService<PersistenceDbContext>());
|
services.AddScoped<DbContext>(provider => provider.GetRequiredService<PersistencePostgresContext>());
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
28
Persistence.Database.Postgres/DesignTimeDbContextFactory.cs
Normal file
28
Persistence.Database.Postgres/DesignTimeDbContextFactory.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
using Npgsql;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
|
||||||
|
namespace Persistence.Database.Postgres;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Фабрика контекста для dotnet ef миграций
|
||||||
|
/// </summary>
|
||||||
|
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<PersistencePostgresContext>
|
||||||
|
{
|
||||||
|
public PersistencePostgresContext CreateDbContext(string[] args)
|
||||||
|
{
|
||||||
|
var connectionStringBuilder = new NpgsqlConnectionStringBuilder();
|
||||||
|
connectionStringBuilder.Host = "localhost";
|
||||||
|
connectionStringBuilder.Database = "persistence";
|
||||||
|
connectionStringBuilder.Username = "postgres";
|
||||||
|
connectionStringBuilder.Password = "q";
|
||||||
|
connectionStringBuilder.PersistSecurityInfo = true;
|
||||||
|
var connectionString = connectionStringBuilder.ToString();
|
||||||
|
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<PersistencePostgresContext>();
|
||||||
|
optionsBuilder.UseNpgsql(connectionString);
|
||||||
|
var context = new PersistencePostgresContext(optionsBuilder.Options);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
@ -49,10 +49,13 @@ public static class EFExtensionsInitialization
|
|||||||
var migrations = db.GetPendingMigrations()
|
var migrations = db.GetPendingMigrations()
|
||||||
.Select(migration => $" ('{migration}', '{efVersionString}')");
|
.Select(migration => $" ('{migration}', '{efVersionString}')");
|
||||||
|
|
||||||
var sqlAddLastMigration =
|
if (migrations.Any())
|
||||||
$"INSERT INTO public.\"__EFMigrationsHistory\" " +
|
{
|
||||||
$"(\"MigrationId\", \"ProductVersion\") " +
|
var sqlAddLastMigration =
|
||||||
$"VALUES {string.Join(',', migrations)};";
|
$"INSERT INTO public.\"__EFMigrationsHistory\" " +
|
||||||
db.ExecuteSqlRaw(sqlAddLastMigration);
|
$"(\"MigrationId\", \"ProductVersion\") " +
|
||||||
|
$"VALUES {string.Join(',', migrations)};";
|
||||||
|
db.ExecuteSqlRaw(sqlAddLastMigration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@ -12,8 +13,8 @@ using Persistence.Database.Model;
|
|||||||
namespace Persistence.Database.Postgres.Migrations
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PersistenceDbContext))]
|
[DbContext(typeof(PersistenceDbContext))]
|
||||||
[Migration("20241122074646_InitialCreate")]
|
[Migration("20241126071115_Add_ChangeLog")]
|
||||||
partial class InitialCreate
|
partial class Add_ChangeLog
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@ -27,6 +28,59 @@ namespace Persistence.Database.Postgres.Migrations
|
|||||||
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
|
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Persistence.Database.Model.ChangeLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Creation")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("Creation");
|
||||||
|
|
||||||
|
b.Property<double>("DepthEnd")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("DepthEnd");
|
||||||
|
|
||||||
|
b.Property<double>("DepthStart")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("DepthStart");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdAuthor")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdAuthor");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdDiscriminator")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdDiscriminator");
|
||||||
|
|
||||||
|
b.Property<Guid?>("IdEditor")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdEditor");
|
||||||
|
|
||||||
|
b.Property<Guid?>("IdNext")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdNext");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdSection")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdSection");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("Obsolete")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("Obsolete");
|
||||||
|
|
||||||
|
b.Property<IDictionary<string, object>>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasColumnName("Value");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ChangeLog");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
||||||
{
|
{
|
||||||
b.Property<DateTimeOffset>("Date")
|
b.Property<DateTimeOffset>("Date")
|
@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Add_ChangeLog : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ChangeLog",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
IdAuthor = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
IdEditor = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
Obsolete = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
IdNext = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
DepthStart = table.Column<double>(type: "double precision", nullable: false),
|
||||||
|
DepthEnd = table.Column<double>(type: "double precision", nullable: false),
|
||||||
|
IdSection = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Value = table.Column<IDictionary<string, object>>(type: "jsonb", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ChangeLog", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ChangeLog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,9 +11,9 @@ using Persistence.Database.Model;
|
|||||||
|
|
||||||
namespace Persistence.Database.Postgres.Migrations
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PersistenceDbContext))]
|
[DbContext(typeof(PersistencePostgresContext))]
|
||||||
[Migration("20241118052225_SetpointMigration")]
|
[Migration("20241126100631_Init")]
|
||||||
partial class SetpointMigration
|
partial class Init
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@ -27,14 +27,34 @@ namespace Persistence.Database.Postgres.Migrations
|
|||||||
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
|
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Persistence.Database.Entity.TimestampedSet", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("IdDiscriminator")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Дискриминатор ссылка на тип сохраняемых данных");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Timestamp")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("Отметка времени, строго в UTC");
|
||||||
|
|
||||||
|
b.Property<string>("Set")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasComment("Набор сохраняемых данных");
|
||||||
|
|
||||||
|
b.HasKey("IdDiscriminator", "Timestamp");
|
||||||
|
|
||||||
|
b.ToTable("TimestampedSets", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("Общая таблица данных временных рядов");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<DateTimeOffset>("Date")
|
||||||
.ValueGeneratedOnAdd()
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnType("integer")
|
.HasColumnName("date");
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<double?>("AxialLoad")
|
b.Property<double?>("AxialLoad")
|
||||||
.HasColumnType("double precision")
|
.HasColumnType("double precision")
|
||||||
@ -100,10 +120,6 @@ namespace Persistence.Database.Postgres.Migrations
|
|||||||
.HasColumnType("double precision")
|
.HasColumnType("double precision")
|
||||||
.HasColumnName("rotorTorque");
|
.HasColumnName("rotorTorque");
|
||||||
|
|
||||||
b.Property<int>("TimeStamp")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("timestamp");
|
|
||||||
|
|
||||||
b.Property<string>("User")
|
b.Property<string>("User")
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
.HasColumnName("user");
|
.HasColumnName("user");
|
||||||
@ -112,7 +128,7 @@ namespace Persistence.Database.Postgres.Migrations
|
|||||||
.HasColumnType("double precision")
|
.HasColumnType("double precision")
|
||||||
.HasColumnName("wellDepth");
|
.HasColumnName("wellDepth");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Date");
|
||||||
|
|
||||||
b.ToTable("DataSaub");
|
b.ToTable("DataSaub");
|
||||||
});
|
});
|
||||||
@ -125,7 +141,7 @@ namespace Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.Property<DateTimeOffset>("Created")
|
b.Property<DateTimeOffset>("Created")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasComment("Дата изменения уставки");
|
.HasComment("Дата создания уставки");
|
||||||
|
|
||||||
b.Property<int>("IdUser")
|
b.Property<int>("IdUser")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||||||
namespace Persistence.Database.Postgres.Migrations
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class InitialCreate : Migration
|
public partial class Init : Migration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
@ -42,6 +42,34 @@ namespace Persistence.Database.Postgres.Migrations
|
|||||||
{
|
{
|
||||||
table.PrimaryKey("PK_DataSaub", x => x.date);
|
table.PrimaryKey("PK_DataSaub", x => x.date);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Setpoint",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Key = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ"),
|
||||||
|
Created = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания уставки"),
|
||||||
|
Value = table.Column<object>(type: "jsonb", nullable: false, comment: "Значение уставки"),
|
||||||
|
IdUser = table.Column<int>(type: "integer", nullable: false, comment: "Id автора последнего изменения")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Created });
|
||||||
|
});
|
||||||
|
|
||||||
|
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: "Общая таблица данных временных рядов");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -49,6 +77,12 @@ namespace Persistence.Database.Postgres.Migrations
|
|||||||
{
|
{
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "DataSaub");
|
name: "DataSaub");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Setpoint");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TimestampedSets");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
@ -10,8 +11,8 @@ using Persistence.Database.Model;
|
|||||||
|
|
||||||
namespace Persistence.Database.Postgres.Migrations
|
namespace Persistence.Database.Postgres.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PersistenceDbContext))]
|
[DbContext(typeof(PersistencePostgresContext))]
|
||||||
partial class PersistenceDbContextModelSnapshot : ModelSnapshot
|
partial class PersistencePostgresContextModelSnapshot : ModelSnapshot
|
||||||
{
|
{
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
@ -131,6 +132,59 @@ namespace Persistence.Database.Postgres.Migrations
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Persistence.Database.Model.ChangeLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Creation")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("Creation");
|
||||||
|
|
||||||
|
b.Property<double>("DepthEnd")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("DepthEnd");
|
||||||
|
|
||||||
|
b.Property<double>("DepthStart")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("DepthStart");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdAuthor")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdAuthor");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdDiscriminator")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdDiscriminator");
|
||||||
|
|
||||||
|
b.Property<Guid?>("IdEditor")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdEditor");
|
||||||
|
|
||||||
|
b.Property<Guid?>("IdNext")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdNext");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdSection")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("IdSection");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("Obsolete")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("Obsolete");
|
||||||
|
|
||||||
|
b.Property<IDictionary<string, object>>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasColumnName("Value");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ChangeLog");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
|
||||||
{
|
{
|
||||||
b.Property<DateTimeOffset>("Date")
|
b.Property<DateTimeOffset>("Date")
|
@ -1,55 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Persistence.Database.Entity;
|
|
||||||
|
|
||||||
namespace Persistence.Database.Model;
|
|
||||||
public partial class PersistenceDbContext : DbContext
|
|
||||||
{
|
|
||||||
public DbSet<DataSaub> DataSaub => Set<DataSaub>();
|
|
||||||
|
|
||||||
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
|
||||||
|
|
||||||
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
|
|
||||||
|
|
||||||
public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>();
|
|
||||||
|
|
||||||
public DbSet<ParameterData> ParameterData => Set<ParameterData>();
|
|
||||||
|
|
||||||
public PersistenceDbContext()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public PersistenceDbContext(DbContextOptions<PersistenceDbContext> options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
|
||||||
{
|
|
||||||
if (!optionsBuilder.IsConfigured)
|
|
||||||
optionsBuilder.UseNpgsql("Host=localhost;Database=persistence;Username=postgres;Password=q;Persist Security Info=True;Include Error Detail=True;"
|
|
||||||
//, builder=>builder.EnableRetryOnFailure(2, System.TimeSpan.FromMinutes(1))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
modelBuilder.HasPostgresExtension("adminpack")
|
|
||||||
.HasAnnotation("Relational:Collation", "Russian_Russia.1251");
|
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
23
Persistence.Database.Postgres/PersistencePostgresContext.cs
Normal file
23
Persistence.Database.Postgres/PersistencePostgresContext.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure.Internal;
|
||||||
|
|
||||||
|
namespace Persistence.Database.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EF êîíòåêñò äëÿ ÁÄ Postgres
|
||||||
|
/// </summary>
|
||||||
|
public partial class PersistencePostgresContext : PersistenceDbContext
|
||||||
|
{
|
||||||
|
public PersistencePostgresContext(DbContextOptions options)
|
||||||
|
: base(options)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.HasPostgresExtension("adminpack")
|
||||||
|
.HasAnnotation("Relational:Collation", "Russian_Russia.1251");
|
||||||
|
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -3,3 +3,9 @@
|
|||||||
dotnet ef migrations add <MigrationName> --project Persistence.Database.Postgres
|
dotnet ef migrations add <MigrationName> --project Persistence.Database.Postgres
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Откатить миграцию
|
||||||
|
```
|
||||||
|
dotnet ef migrations remove --project Persistence.Database.Postgres
|
||||||
|
```
|
||||||
|
Удаляется последняя созданная миграция.
|
46
Persistence.Database/Entity/ChangeLog.cs
Normal file
46
Persistence.Database/Entity/ChangeLog.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Persistence.Models;
|
||||||
|
|
||||||
|
namespace Persistence.Database.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Часть записи, описывающая изменение
|
||||||
|
/// </summary>
|
||||||
|
public class ChangeLog : IChangeLog, IWithSectionPart
|
||||||
|
{
|
||||||
|
[Key, Comment("Ключ записи")]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Comment("Дискриминатор таблицы")]
|
||||||
|
public Guid IdDiscriminator { get; set; }
|
||||||
|
|
||||||
|
[Comment("Автор изменения")]
|
||||||
|
public Guid IdAuthor { get; set; }
|
||||||
|
|
||||||
|
[Comment("Редактор")]
|
||||||
|
public Guid? IdEditor { get; set; }
|
||||||
|
|
||||||
|
[Comment("Дата создания записи")]
|
||||||
|
public DateTimeOffset Creation { get; set; }
|
||||||
|
|
||||||
|
[Comment("Дата устаревания (например при удалении)")]
|
||||||
|
public DateTimeOffset? Obsolete { get; set; }
|
||||||
|
|
||||||
|
[Comment("Id заменяющей записи")]
|
||||||
|
public Guid? IdNext { get; set; }
|
||||||
|
|
||||||
|
[Comment("Глубина забоя на дату начала интервала")]
|
||||||
|
public double DepthStart { get; set; }
|
||||||
|
|
||||||
|
[Comment("Глубина забоя на дату окончания интервала")]
|
||||||
|
public double DepthEnd { get; set; }
|
||||||
|
|
||||||
|
[Comment("Ключ секции")]
|
||||||
|
public Guid IdSection { get; set; }
|
||||||
|
|
||||||
|
[Column(TypeName = "jsonb"), Comment("Значение")]
|
||||||
|
public required IDictionary<string, object> Value { get; set; }
|
||||||
|
}
|
48
Persistence.Database/Entity/IChangeLog.cs
Normal file
48
Persistence.Database/Entity/IChangeLog.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
namespace Persistence.Database.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Часть записи, описывающая изменение
|
||||||
|
/// </summary>
|
||||||
|
public interface IChangeLog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ключ записи
|
||||||
|
/// </summary>
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Автор изменения
|
||||||
|
/// </summary>
|
||||||
|
public Guid IdAuthor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Редактор
|
||||||
|
/// </summary>
|
||||||
|
public Guid? IdEditor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата создания записи
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset Creation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата устаревания (например при удалении)
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset? Obsolete { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id заменяющей записи
|
||||||
|
/// </summary>
|
||||||
|
public Guid? IdNext { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дискриминатор таблицы
|
||||||
|
/// </summary>
|
||||||
|
public Guid IdDiscriminator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Значение
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, object> Value { get; set; }
|
||||||
|
}
|
@ -1,10 +1,4 @@
|
|||||||
using System;
|
namespace Persistence.Database.Model;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.Database.Model;
|
|
||||||
public interface ITimestampedData
|
public interface ITimestampedData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -14,4 +14,8 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Persistence\Persistence.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
36
Persistence.Database/PersistenceDbContext.cs
Normal file
36
Persistence.Database/PersistenceDbContext.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Persistence.Database.Entity;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
|
||||||
|
namespace Persistence.Database;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EF контекст для любых БД поддерживаемых в EF
|
||||||
|
/// </summary>
|
||||||
|
public class PersistenceDbContext : DbContext
|
||||||
|
{
|
||||||
|
public DbSet<DataSaub> DataSaub => Set<DataSaub>();
|
||||||
|
|
||||||
|
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
||||||
|
|
||||||
|
public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>();
|
||||||
|
|
||||||
|
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
|
||||||
|
|
||||||
|
public PersistenceDbContext(DbContextOptions options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<TimestampedSet>()
|
||||||
|
.Property(e => e.Set)
|
||||||
|
.HasJsonConversion();
|
||||||
|
|
||||||
|
modelBuilder.Entity<ChangeLog>()
|
||||||
|
.Property(e => e.Value)
|
||||||
|
.HasJsonConversion();
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,7 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Persistence.Database;
|
||||||
using Persistence.Database.Model;
|
using Persistence.Database.Model;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Persistence.IntegrationTests;
|
namespace Persistence.IntegrationTests;
|
||||||
@ -19,7 +16,7 @@ public abstract class BaseIntegrationTest : IClassFixture<WebAppFactoryFixture>,
|
|||||||
{
|
{
|
||||||
scope = factory.Services.CreateScope();
|
scope = factory.Services.CreateScope();
|
||||||
|
|
||||||
dbContext = scope.ServiceProvider.GetRequiredService<PersistenceDbContext>();
|
dbContext = scope.ServiceProvider.GetRequiredService<PersistencePostgresContext>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -0,0 +1,351 @@
|
|||||||
|
using Mapster;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Persistence.Client;
|
||||||
|
using Persistence.Client.Clients;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
|
using System.Net;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Persistence.IntegrationTests.Controllers;
|
||||||
|
public class ChangeLogControllerTest : BaseIntegrationTest
|
||||||
|
{
|
||||||
|
private readonly IChangeLogClient client;
|
||||||
|
private static Random generatorRandomDigits = new Random();
|
||||||
|
|
||||||
|
public ChangeLogControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||||
|
{
|
||||||
|
var persistenceClientFactory = scope.ServiceProvider
|
||||||
|
.GetRequiredService<PersistenceClientFactory>();
|
||||||
|
|
||||||
|
client = persistenceClientFactory.GetClient<IChangeLogClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ClearAndInsertRange_InEmptyDb()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var idDiscriminator = Guid.NewGuid();
|
||||||
|
var dtos = Generate(2, DateTimeOffset.UtcNow);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.ClearAndAddRange(idDiscriminator, dtos);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
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<DataWithWellDepthAndSectionDto>());
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var count = 1;
|
||||||
|
var idDiscriminator = Guid.NewGuid();
|
||||||
|
var dtos = Generate(count, DateTimeOffset.UtcNow);
|
||||||
|
var dto = dtos.FirstOrDefault()!;
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.Add(idDiscriminator, dto);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(HttpStatusCode.Created, result.StatusCode);
|
||||||
|
Assert.Equal(count, result.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddRange_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var count = 3;
|
||||||
|
var idDiscriminator = Guid.NewGuid();
|
||||||
|
var dtos = Generate(count, DateTimeOffset.UtcNow);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.AddRange(idDiscriminator, dtos);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(HttpStatusCode.Created, result.StatusCode);
|
||||||
|
Assert.Equal(count, result.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Update_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var idDiscriminator = Guid.NewGuid();
|
||||||
|
var dtos = Generate(1, DateTimeOffset.UtcNow);
|
||||||
|
var dto = dtos.FirstOrDefault()!;
|
||||||
|
var result = await client.Add(idDiscriminator, dto);
|
||||||
|
Assert.Equal(HttpStatusCode.Created, result.StatusCode);
|
||||||
|
|
||||||
|
var entity = dbContext.ChangeLog
|
||||||
|
.Where(x => x.IdDiscriminator == idDiscriminator)
|
||||||
|
.FirstOrDefault();
|
||||||
|
dto = entity.Adapt<DataWithWellDepthAndSectionDto>();
|
||||||
|
dto.DepthEnd = dto.DepthEnd + 10;
|
||||||
|
|
||||||
|
// act
|
||||||
|
result = await client.Update(dto);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
Assert.Equal(2, result.Content);
|
||||||
|
|
||||||
|
var dateBegin = DateTimeOffset.UtcNow.AddDays(-1);
|
||||||
|
var dateEnd = DateTimeOffset.UtcNow.AddDays(1);
|
||||||
|
|
||||||
|
var changeLogResult = await client.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, changeLogResult.StatusCode);
|
||||||
|
Assert.NotNull(changeLogResult.Content);
|
||||||
|
|
||||||
|
var changeLogDtos = changeLogResult.Content;
|
||||||
|
|
||||||
|
var obsoleteDto = changeLogDtos
|
||||||
|
.Where(e => e.Obsolete.HasValue)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
var activeDto = changeLogDtos
|
||||||
|
.Where(e => !e.Obsolete.HasValue)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (obsoleteDto == null || activeDto == null)
|
||||||
|
{
|
||||||
|
Assert.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(activeDto.Id, obsoleteDto.IdNext);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateRange_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var count = 2;
|
||||||
|
var dtos = Generate(count, DateTimeOffset.UtcNow);
|
||||||
|
var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray();
|
||||||
|
dbContext.ChangeLog.AddRange(entities);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
dtos = entities.Select(c => new DataWithWellDepthAndSectionDto()
|
||||||
|
{
|
||||||
|
DepthEnd = c.DepthEnd + 10,
|
||||||
|
DepthStart = c.DepthStart + 10,
|
||||||
|
Id = c.Id,
|
||||||
|
IdSection = c.IdSection,
|
||||||
|
Value = c.Value
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.UpdateRange(dtos);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
Assert.Equal(count * 2, result.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Delete_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var dtos = Generate(1, DateTimeOffset.UtcNow);
|
||||||
|
var dto = dtos.FirstOrDefault()!;
|
||||||
|
var entity = dto.Adapt<ChangeLog>();
|
||||||
|
dbContext.ChangeLog.Add(entity);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.Delete(entity.Id);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
Assert.Equal(1, result.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeleteRange_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var count = 10;
|
||||||
|
var dtos = Generate(count, DateTimeOffset.UtcNow);
|
||||||
|
var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray();
|
||||||
|
dbContext.ChangeLog.AddRange(entities);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var ids = entities.Select(e => e.Id);
|
||||||
|
var result = await client.DeleteRange(ids);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
Assert.Equal(count, result.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetDatesRange_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var changeLogItems = CreateChangeLogItems(3, (-15, 15));
|
||||||
|
var idDiscriminator = changeLogItems.Item1;
|
||||||
|
var entities = changeLogItems.Item2.OrderBy(e => e.Creation);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = await client.GetDatesRange(idDiscriminator);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
Assert.NotNull(result.Content);
|
||||||
|
|
||||||
|
var minDate = entities.First().Creation;
|
||||||
|
var maxDate = entities.Last().Creation;
|
||||||
|
|
||||||
|
var expectedMinDate = minDate.ToUniversalTime().ToString();
|
||||||
|
var actualMinDate = result.Content.From.ToUniversalTime().ToString();
|
||||||
|
Assert.Equal(expectedMinDate, actualMinDate);
|
||||||
|
|
||||||
|
var expectedMaxDate = maxDate.ToUniversalTime().ToString();
|
||||||
|
var actualMaxDate = result.Content.To.ToUniversalTime().ToString();
|
||||||
|
Assert.Equal(expectedMaxDate, actualMaxDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetByDate_returns_success()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
//создаем записи
|
||||||
|
var count = 5;
|
||||||
|
var changeLogItems = CreateChangeLogItems(count, (-15, 15));
|
||||||
|
var idDiscriminator = changeLogItems.Item1;
|
||||||
|
var entities = changeLogItems.Item2;
|
||||||
|
|
||||||
|
//удаляем все созданные записи за исключением первой и второй
|
||||||
|
//даты 2-х оставшихся записей должны вернуться в методе GetByDate
|
||||||
|
var ids = entities.Select(e => e.Id);
|
||||||
|
var idsToDelete = ids.Skip(2);
|
||||||
|
|
||||||
|
var deletedCount = await client.DeleteRange(idsToDelete);
|
||||||
|
|
||||||
|
var filterRequest = new SectionPartRequest()
|
||||||
|
{
|
||||||
|
DepthStart = 0,
|
||||||
|
DepthEnd = 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
var paginationRequest = new PaginationRequest()
|
||||||
|
{
|
||||||
|
Skip = 0,
|
||||||
|
Take = 10,
|
||||||
|
SortSettings = String.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
var moment = DateTimeOffset.UtcNow.AddDays(16);
|
||||||
|
var result = await client.GetByDate(idDiscriminator, moment, filterRequest, paginationRequest);
|
||||||
|
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
Assert.NotNull(result.Content);
|
||||||
|
|
||||||
|
var restEntities = entities.Where(e => !idsToDelete.Contains(e.Id));
|
||||||
|
Assert.Equal(restEntities.Count(), result.Content.Count);
|
||||||
|
|
||||||
|
var actualIds = restEntities.Select(e => e.Id);
|
||||||
|
var expectedIds = result.Content.Items.Select(e => e.Id);
|
||||||
|
Assert.Equivalent(expectedIds, actualIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(5, -15, 15, -20, 20, 10)]
|
||||||
|
[InlineData(5, -15, -10, -16, -9, 5)]
|
||||||
|
public async Task GetChangeLogForInterval_returns_success(
|
||||||
|
int insertedCount,
|
||||||
|
int daysBeforeNowChangeLog,
|
||||||
|
int daysAfterNowChangeLog,
|
||||||
|
int daysBeforeNowFilter,
|
||||||
|
int daysAfterNowFilter,
|
||||||
|
int changeLogCount)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
//создаем записи
|
||||||
|
var count = insertedCount;
|
||||||
|
var daysRange = (daysBeforeNowChangeLog, daysAfterNowChangeLog);
|
||||||
|
var changeLogItems = CreateChangeLogItems(count, daysRange);
|
||||||
|
var idDiscriminator = changeLogItems.Item1;
|
||||||
|
var entities = changeLogItems.Item2;
|
||||||
|
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
entity.DepthEnd = entity.DepthEnd + 10;
|
||||||
|
}
|
||||||
|
var dtos = entities.Select(e => e.Adapt<DataWithWellDepthAndSectionDto>()).ToArray();
|
||||||
|
await client.UpdateRange(dtos);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var dateBegin = DateTimeOffset.UtcNow.AddDays(daysBeforeNowFilter);
|
||||||
|
var dateEnd = DateTimeOffset.UtcNow.AddDays(daysAfterNowFilter);
|
||||||
|
var result = await client.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
Assert.NotNull(result.Content);
|
||||||
|
Assert.Equal(changeLogCount, result.Content.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static IEnumerable<DataWithWellDepthAndSectionDto> Generate(int count, DateTimeOffset from)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
yield return new DataWithWellDepthAndSectionDto()
|
||||||
|
{
|
||||||
|
Value = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "Key", 1 }
|
||||||
|
},
|
||||||
|
DepthStart = generatorRandomDigits.Next(1, 5),
|
||||||
|
DepthEnd = generatorRandomDigits.Next(5, 15),
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
IdSection = Guid.NewGuid()
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private (Guid, ChangeLog[]) CreateChangeLogItems(int count, (int, int) daysRange)
|
||||||
|
{
|
||||||
|
var minDayCount = daysRange.Item1;
|
||||||
|
var maxDayCount = daysRange.Item2;
|
||||||
|
|
||||||
|
Guid idDiscriminator = Guid.NewGuid();
|
||||||
|
var dtos = Generate(count, DateTimeOffset.UtcNow);
|
||||||
|
var entities = dtos.Select(d =>
|
||||||
|
{
|
||||||
|
var entity = d.Adapt<ChangeLog>();
|
||||||
|
entity.IdDiscriminator = idDiscriminator;
|
||||||
|
entity.Creation = DateTimeOffset.UtcNow.AddDays(generatorRandomDigits.Next(minDayCount, maxDayCount));
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}).ToArray();
|
||||||
|
dbContext.ChangeLog.AddRange(entities);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
return (idDiscriminator, entities);
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
using System.Net;
|
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 Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Persistence.IntegrationTests.Controllers
|
namespace Persistence.IntegrationTests.Controllers
|
||||||
@ -32,7 +33,7 @@ namespace Persistence.IntegrationTests.Controllers
|
|||||||
dbContext.CleanupDbSet<TechMessage>();
|
dbContext.CleanupDbSet<TechMessage>();
|
||||||
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
|
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
|
||||||
|
|
||||||
var requestDto = new RequestDto()
|
var PaginationRequest = new PaginationRequest()
|
||||||
{
|
{
|
||||||
Skip = 1,
|
Skip = 1,
|
||||||
Take = 2,
|
Take = 2,
|
||||||
@ -46,8 +47,8 @@ namespace Persistence.IntegrationTests.Controllers
|
|||||||
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(requestDto.Skip, response.Content.Skip);
|
Assert.Equal(PaginationRequest.Skip, response.Content.Skip);
|
||||||
Assert.Equal(requestDto.Take, response.Content.Take);
|
Assert.Equal(PaginationRequest.Take, response.Content.Take);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -56,7 +57,7 @@ namespace Persistence.IntegrationTests.Controllers
|
|||||||
//arrange
|
//arrange
|
||||||
var dtos = await InsertRange();
|
var dtos = await InsertRange();
|
||||||
var dtosCount = dtos.Count();
|
var dtosCount = dtos.Count();
|
||||||
var requestDto = new RequestDto()
|
var PaginationRequest = new PaginationRequest()
|
||||||
{
|
{
|
||||||
Skip = 0,
|
Skip = 0,
|
||||||
Take = 2,
|
Take = 2,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Persistence.Database.Model;
|
using Persistence.Database;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
|
||||||
namespace Persistence.IntegrationTests.Extensions;
|
namespace Persistence.IntegrationTests.Extensions;
|
||||||
|
|
||||||
public static class EFCoreExtensions
|
public static class EFCoreExtensions
|
||||||
{
|
{
|
||||||
public static void CleanupDbSet<T>(this PersistenceDbContext dbContext)
|
public static void CleanupDbSet<T>(this PersistenceDbContext dbContext)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
var dbset = dbContext.Set<T>();
|
var dbset = dbContext.Set<T>();
|
||||||
|
@ -5,7 +5,6 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Persistence.API;
|
using Persistence.API;
|
||||||
using Persistence.Database;
|
|
||||||
using Persistence.Client;
|
using Persistence.Client;
|
||||||
using Persistence.Database.Model;
|
using Persistence.Database.Model;
|
||||||
using Persistence.Database.Postgres;
|
using Persistence.Database.Postgres;
|
||||||
@ -28,10 +27,11 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
|
|||||||
|
|
||||||
builder.ConfigureServices(services =>
|
builder.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<PersistenceDbContext>));
|
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<PersistencePostgresContext>));
|
||||||
if (descriptor != null)
|
if (descriptor != null)
|
||||||
services.Remove(descriptor);
|
services.Remove(descriptor);
|
||||||
services.AddDbContext<PersistenceDbContext>(options =>
|
|
||||||
|
services.AddDbContext<PersistencePostgresContext>(options =>
|
||||||
options.UseNpgsql(connectionString));
|
options.UseNpgsql(connectionString));
|
||||||
|
|
||||||
services.RemoveAll<IHttpClientFactory>();
|
services.RemoveAll<IHttpClientFactory>();
|
||||||
@ -47,7 +47,7 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
|
|||||||
using var scope = serviceProvider.CreateScope();
|
using var scope = serviceProvider.CreateScope();
|
||||||
var scopedServices = scope.ServiceProvider;
|
var scopedServices = scope.ServiceProvider;
|
||||||
|
|
||||||
var dbContext = scopedServices.GetRequiredService<PersistenceDbContext>();
|
var dbContext = scopedServices.GetRequiredService<PersistencePostgresContext>();
|
||||||
dbContext.Database.EnsureCreatedAndMigrated();
|
dbContext.Database.EnsureCreatedAndMigrated();
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
});
|
});
|
||||||
@ -55,8 +55,8 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
|
|||||||
|
|
||||||
public override async ValueTask DisposeAsync()
|
public override async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
var dbContext = new PersistenceDbContext(
|
var dbContext = new PersistencePostgresContext(
|
||||||
new DbContextOptionsBuilder<PersistenceDbContext>()
|
new DbContextOptionsBuilder<PersistencePostgresContext>()
|
||||||
.UseNpgsql(connectionString)
|
.UseNpgsql(connectionString)
|
||||||
.Options);
|
.Options);
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
using Mapster;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Persistence.Database.Model;
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Models;
|
||||||
using Persistence.Repositories;
|
using Persistence.Repositories;
|
||||||
using Persistence.Repository.Data;
|
using Persistence.Repository.Data;
|
||||||
using Persistence.Repository.Repositories;
|
using Persistence.Repository.Repositories;
|
||||||
@ -9,6 +11,17 @@ public static class DependencyInjection
|
|||||||
{
|
{
|
||||||
public static void MapsterSetup()
|
public static void MapsterSetup()
|
||||||
{
|
{
|
||||||
|
TypeAdapterConfig.GlobalSettings.Default.Config
|
||||||
|
.ForType<ChangeLog, ChangeLogDto>()
|
||||||
|
.Map(dest => dest.Value, src => new DataWithWellDepthAndSectionDto()
|
||||||
|
{
|
||||||
|
DepthEnd = src.DepthEnd,
|
||||||
|
DepthStart = src.DepthStart,
|
||||||
|
IdSection = src.IdSection,
|
||||||
|
Value = src.Value,
|
||||||
|
Id = src.Id
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
||||||
@ -18,6 +31,7 @@ public static class DependencyInjection
|
|||||||
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataRepository<DataSaub, DataSaubDto>>();
|
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataRepository<DataSaub, DataSaubDto>>();
|
||||||
services.AddTransient<ISetpointRepository, SetpointRepository>();
|
services.AddTransient<ISetpointRepository, SetpointRepository>();
|
||||||
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataCachedRepository<DataSaub, DataSaubDto>>();
|
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataCachedRepository<DataSaub, DataSaubDto>>();
|
||||||
|
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
|
||||||
services.AddTransient<ITimestampedSetRepository, TimestampedSetRepository>();
|
services.AddTransient<ITimestampedSetRepository, TimestampedSetRepository>();
|
||||||
services.AddTransient<ITechMessagesRepository, TechMessagesRepository>();
|
services.AddTransient<ITechMessagesRepository, TechMessagesRepository>();
|
||||||
services.AddTransient<IParameterRepository, ParameterRepository>();
|
services.AddTransient<IParameterRepository, ParameterRepository>();
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||||
|
<PackageReference Include="UuidExtensions" Version="1.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
82
Persistence.Repository/QueryBuilders.cs
Normal file
82
Persistence.Repository/QueryBuilders.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
|
|
||||||
|
namespace Persistence.Repository;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// класс с набором методов, необходимых для фильтрации записей
|
||||||
|
/// </summary>
|
||||||
|
public static class QueryBuilders
|
||||||
|
{
|
||||||
|
public static IQueryable<TEntity> Apply<TEntity>(this IQueryable<TEntity> query, SectionPartRequest request)
|
||||||
|
where TEntity : class, IWithSectionPart
|
||||||
|
{
|
||||||
|
if (request.IdSection.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(e => e.IdSection == request.IdSection);
|
||||||
|
}
|
||||||
|
if (request.DepthStart.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(e => e.DepthStart >= request.DepthStart);
|
||||||
|
}
|
||||||
|
if (request.DepthEnd.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(e => e.DepthEnd <= request.DepthEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<TEntity> Apply<TEntity>(this IQueryable<TEntity> query,DateTimeOffset momentUtc)
|
||||||
|
where TEntity : class, IChangeLog
|
||||||
|
{
|
||||||
|
momentUtc = momentUtc.ToUniversalTime();
|
||||||
|
|
||||||
|
query = query
|
||||||
|
.Where(e => e.Creation <= momentUtc)
|
||||||
|
.Where(e => e.Obsolete == null || e.Obsolete >= momentUtc);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task<PaginationContainer<TDto>> ApplyPagination<TEntity, TDto>(
|
||||||
|
this IQueryable<TEntity> query,
|
||||||
|
PaginationRequest request,
|
||||||
|
Func<TEntity, TDto> Convert,
|
||||||
|
CancellationToken token)
|
||||||
|
where TEntity : class, IWithSectionPart
|
||||||
|
where TDto : class
|
||||||
|
{
|
||||||
|
if (String.IsNullOrEmpty(request.SortSettings))
|
||||||
|
{
|
||||||
|
query = query
|
||||||
|
.OrderBy(e => e.IdSection)
|
||||||
|
.ThenBy(e => e.DepthStart)
|
||||||
|
.ThenBy(e => e.DepthEnd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query = query.SortBy(request.SortSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entities = await query
|
||||||
|
.Skip(request.Skip)
|
||||||
|
.Take(request.Take)
|
||||||
|
.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var count = await query.CountAsync(token);
|
||||||
|
var items = entities.Select(Convert);
|
||||||
|
var result = new PaginationContainer<TDto>
|
||||||
|
{
|
||||||
|
Skip = request.Skip,
|
||||||
|
Take = request.Take,
|
||||||
|
Items = items,
|
||||||
|
Count = count
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
259
Persistence.Repository/Repositories/ChangeLogRepository.cs
Normal file
259
Persistence.Repository/Repositories/ChangeLogRepository.cs
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
using Mapster;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Persistence.Database.Model;
|
||||||
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
|
using Persistence.Repositories;
|
||||||
|
using UuidExtensions;
|
||||||
|
|
||||||
|
namespace Persistence.Repository.Repositories;
|
||||||
|
public class ChangeLogRepository : IChangeLogRepository
|
||||||
|
{
|
||||||
|
private DbContext db;
|
||||||
|
|
||||||
|
public ChangeLogRepository(DbContext db)
|
||||||
|
{
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token)
|
||||||
|
{
|
||||||
|
var entities = new List<ChangeLog>();
|
||||||
|
foreach (var dto in dtos)
|
||||||
|
{
|
||||||
|
var entity = CreateEntityFromDto(idAuthor, idDiscriminator, dto);
|
||||||
|
entities.Add(entity);
|
||||||
|
}
|
||||||
|
db.Set<ChangeLog>().AddRange(entities);
|
||||||
|
|
||||||
|
var result = await db.SaveChangesAsync(token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> MarkAsDeleted(Guid idEditor, IEnumerable<Guid> ids, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>()
|
||||||
|
.Where(s => ids.Contains(s.Id))
|
||||||
|
.Where(s => s.Obsolete == null);
|
||||||
|
|
||||||
|
if (query.Count() != ids.Count())
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Count of active items not equal count of ids", nameof(ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var result = await MarkAsObsolete(idEditor, entities, token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>()
|
||||||
|
.Where(s => s.IdDiscriminator == idDiscriminator)
|
||||||
|
.Where(e => e.Obsolete == null);
|
||||||
|
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var result = await MarkAsObsolete(idEditor, entities, token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> MarkAsObsolete(Guid idEditor, IEnumerable<ChangeLog> entities, CancellationToken token)
|
||||||
|
{
|
||||||
|
var updateTime = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
entity.Obsolete = updateTime;
|
||||||
|
entity.IdEditor = idEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await db.SaveChangesAsync(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token)
|
||||||
|
{
|
||||||
|
var result = 0;
|
||||||
|
|
||||||
|
using var transaction = await db.Database.BeginTransactionAsync(token);
|
||||||
|
|
||||||
|
result += await MarkAsDeleted(idAuthor, idDiscriminator, token);
|
||||||
|
result += await AddRange(idAuthor, idDiscriminator, dtos, token);
|
||||||
|
|
||||||
|
await transaction.CommitAsync(token);
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> UpdateRange(Guid idEditor, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token)
|
||||||
|
{
|
||||||
|
var dbSet = db.Set<ChangeLog>();
|
||||||
|
|
||||||
|
var updatedIds = dtos.Select(d => d.Id);
|
||||||
|
var updatedEntities = dbSet
|
||||||
|
.Where(s => updatedIds.Contains(s.Id))
|
||||||
|
.ToDictionary(s => s.Id);
|
||||||
|
|
||||||
|
var result = 0;
|
||||||
|
using var transaction = await db.Database.BeginTransactionAsync(token);
|
||||||
|
|
||||||
|
foreach (var dto in dtos)
|
||||||
|
{
|
||||||
|
var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id);
|
||||||
|
if (updatedEntity is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
var newEntity = CreateEntityFromDto(idEditor, updatedEntity.IdDiscriminator, dto);
|
||||||
|
dbSet.Add(newEntity);
|
||||||
|
|
||||||
|
updatedEntity.IdNext = newEntity.Id;
|
||||||
|
updatedEntity.Obsolete = DateTimeOffset.UtcNow;
|
||||||
|
updatedEntity.IdEditor = idEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await db.SaveChangesAsync(token);
|
||||||
|
await transaction.CommitAsync(token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PaginationContainer<DataWithWellDepthAndSectionDto>> GetByDate(
|
||||||
|
Guid idDiscriminator,
|
||||||
|
DateTimeOffset momentUtc,
|
||||||
|
SectionPartRequest filterRequest,
|
||||||
|
PaginationRequest paginationRequest,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = CreateQuery(idDiscriminator);
|
||||||
|
query = query.Apply(momentUtc);
|
||||||
|
query = query.Apply(filterRequest);
|
||||||
|
|
||||||
|
var result = await query.ApplyPagination(paginationRequest, Convert, token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<ChangeLog> CreateQuery(Guid idDiscriminator)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>().Where(e => e.IdDiscriminator == idDiscriminator);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>().Where(s => s.IdDiscriminator == idDiscriminator);
|
||||||
|
|
||||||
|
var min = new DateTimeOffset(dateBegin.ToUniversalTime().Date, TimeSpan.Zero);
|
||||||
|
var max = new DateTimeOffset(dateEnd.ToUniversalTime().Date, TimeSpan.Zero);
|
||||||
|
|
||||||
|
var createdQuery = query.Where(e => e.Creation >= min && e.Creation <= max);
|
||||||
|
var editedQuery = query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
|
||||||
|
|
||||||
|
query = createdQuery.Union(editedQuery);
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var dtos = entities.Select(e => e.Adapt<ChangeLogDto>());
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>().Where(e => e.IdDiscriminator == idDiscriminator);
|
||||||
|
|
||||||
|
var datesCreateQuery = query
|
||||||
|
.Select(e => e.Creation)
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
var datesCreate = await datesCreateQuery.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var datesUpdateQuery = query
|
||||||
|
.Where(e => e.Obsolete != null)
|
||||||
|
.Select(e => e.Obsolete!.Value)
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
var datesUpdate = await datesUpdateQuery.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var dates = Enumerable.Concat(datesCreate, datesUpdate);
|
||||||
|
var datesOnly = dates
|
||||||
|
.Select(d => new DateOnly(d.Year, d.Month, d.Day))
|
||||||
|
.Distinct()
|
||||||
|
.OrderBy(d => d);
|
||||||
|
|
||||||
|
return datesOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChangeLog CreateEntityFromDto(Guid idAuthor, Guid idDiscriminator, DataWithWellDepthAndSectionDto dto)
|
||||||
|
{
|
||||||
|
var entity = new ChangeLog()
|
||||||
|
{
|
||||||
|
Id = Uuid7.Guid(),
|
||||||
|
Creation = DateTimeOffset.UtcNow,
|
||||||
|
IdAuthor = idAuthor,
|
||||||
|
IdDiscriminator = idDiscriminator,
|
||||||
|
IdEditor = idAuthor,
|
||||||
|
|
||||||
|
Value = dto.Value,
|
||||||
|
IdSection = dto.IdSection,
|
||||||
|
DepthStart = dto.DepthStart,
|
||||||
|
DepthEnd = dto.DepthEnd,
|
||||||
|
};
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<DataWithWellDepthAndSectionDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
|
||||||
|
{
|
||||||
|
var date = dateBegin.ToUniversalTime();
|
||||||
|
var query = this.db.Set<ChangeLog>()
|
||||||
|
.Where(e => e.IdDiscriminator == idDiscriminator)
|
||||||
|
.Where(e => e.Creation >= date || e.Obsolete >= date);
|
||||||
|
|
||||||
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
|
||||||
|
var dtos = entities.Select(Convert);
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
|
||||||
|
{
|
||||||
|
var query = db.Set<ChangeLog>()
|
||||||
|
.Where(e => e.IdDiscriminator == idDiscriminator)
|
||||||
|
.GroupBy(e => 1)
|
||||||
|
.Select(group => new
|
||||||
|
{
|
||||||
|
Min = group.Min(e => e.Creation),
|
||||||
|
Max = group.Max(e => (e.Obsolete.HasValue && e.Obsolete > e.Creation)
|
||||||
|
? e.Obsolete.Value
|
||||||
|
: e.Creation),
|
||||||
|
});
|
||||||
|
|
||||||
|
var values = await query.FirstOrDefaultAsync(token);
|
||||||
|
|
||||||
|
if (values is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DatesRangeDto
|
||||||
|
{
|
||||||
|
From = values.Min,
|
||||||
|
To = values.Max,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataWithWellDepthAndSectionDto Convert(ChangeLog entity) => entity.Adapt<DataWithWellDepthAndSectionDto>();
|
||||||
|
}
|
@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Persistence.Database.Entity;
|
using Persistence.Database.Entity;
|
||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
using Persistence.Repositories;
|
using Persistence.Repositories;
|
||||||
using Persistence.Repository.Extensions;
|
using Persistence.Repository.Extensions;
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ namespace Persistence.Repository.Repositories
|
|||||||
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(RequestDto 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);
|
||||||
|
@ -1,45 +1,71 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
|
|
||||||
namespace Persistence.API;
|
namespace Persistence.API;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Интерфейс для работы с API журнала изменений
|
/// Интерфейс для работы с API журнала изменений
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IChangeLogApi<TDto, TChangeLogDto>
|
public interface IChangeLogApi : ISyncWithDiscriminatorApi<DataWithWellDepthAndSectionDto>
|
||||||
where TDto : class, new()
|
|
||||||
where TChangeLogDto : ChangeLogDto<TDto>
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение исторических данных на текущую дату
|
/// Импорт с заменой: удаление старых строк и добавление новых
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<IEnumerable<TDto>>> GetChangeLogCurrent(CancellationToken token);
|
Task<IActionResult> ClearAndAddRange(Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение исторических данных на определенную дату
|
/// Получение данных на текущую дату (с пагинацией)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="historyMoment"></param>
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="filterRequest">параметры запроса фильтрации</param>
|
||||||
|
/// <param name="paginationRequest">параметры запроса пагинации</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<IEnumerable<TChangeLogDto>>> GetChangeLogForDate(DateTimeOffset historyMoment, CancellationToken token);
|
Task<IActionResult> GetCurrent(Guid idDiscriminator, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение данных на определенную дату (с пагинацией)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="moment"></param>
|
||||||
|
/// <param name="filterRequest">параметры запроса фильтрации</param>
|
||||||
|
/// <param name="paginationRequest">параметры запроса пагинации</param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IActionResult> GetByDate(Guid idDiscriminator, DateTimeOffset moment, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение исторических данных за определенный период времени
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dateBegin"></param>
|
||||||
|
/// <param name="dateEnd"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IActionResult> GetChangeLogForDate(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавить одну запись
|
/// Добавить одну запись
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> Add(TDto dto, CancellationToken token);
|
Task<IActionResult> Add(Guid idDiscriminator, DataWithWellDepthAndSectionDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавить несколько записей
|
/// Добавить несколько записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> AddRange(IEnumerable<TDto> dtos, CancellationToken token);
|
Task<IActionResult> AddRange(Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновить одну запись
|
/// Обновить одну запись
|
||||||
@ -47,7 +73,7 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> Update(TDto dto, CancellationToken token);
|
Task<IActionResult> Update(DataWithWellDepthAndSectionDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновить несколько записей
|
/// Обновить несколько записей
|
||||||
@ -55,7 +81,7 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> UpdateRange(IEnumerable<TDto> dtos, CancellationToken token);
|
Task<IActionResult> UpdateRange(IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Удалить одну запись
|
/// Удалить одну запись
|
||||||
@ -63,7 +89,7 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="id"></param>
|
/// <param name="id"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> Delete(int id, CancellationToken token);
|
Task<IActionResult> Delete(Guid id, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Удалить несколько записей
|
/// Удалить несколько записей
|
||||||
@ -71,5 +97,13 @@ public interface IChangeLogApi<TDto, TChangeLogDto>
|
|||||||
/// <param name="ids"></param>
|
/// <param name="ids"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<int>> DeleteRange(IEnumerable<int> ids, CancellationToken token);
|
Task<IActionResult> DeleteRange(IEnumerable<Guid> ids, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IActionResult> GetDatesChange(Guid idDiscriminator, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
|
|
||||||
namespace Persistence.API;
|
namespace Persistence.API;
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
using System;
|
using Persistence.Models.Requests;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Persistence.API;
|
namespace Persistence.API;
|
||||||
|
|
||||||
/// Интерфейс для API, предназначенного для работы с табличными данными
|
/// Интерфейс для API, предназначенного для работы с табличными данными
|
||||||
public interface ITableDataApi<TDto, TRequest>
|
public interface ITableDataApi<TDto, TRequest>
|
||||||
where TDto : class, new()
|
where TDto : class, new()
|
||||||
where TRequest : RequestDto
|
where TRequest : PaginationRequest
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить страницу списка объектов
|
/// Получить страницу списка объектов
|
||||||
|
114
Persistence/EFExtensions.cs
Normal file
114
Persistence/EFExtensions.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Persistence;
|
||||||
|
public static class EFExtensions
|
||||||
|
{
|
||||||
|
struct TypeAcessor
|
||||||
|
{
|
||||||
|
public LambdaExpression KeySelector { get; set; }
|
||||||
|
public MethodInfo OrderBy { get; set; }
|
||||||
|
public MethodInfo OrderByDescending { get; set; }
|
||||||
|
public MethodInfo ThenBy { get; set; }
|
||||||
|
public MethodInfo ThenByDescending { get; set; }
|
||||||
|
}
|
||||||
|
private static readonly MethodInfo methodOrderBy = GetExtOrderMethod("OrderBy");
|
||||||
|
|
||||||
|
private static readonly MethodInfo methodOrderByDescending = GetExtOrderMethod("OrderByDescending");
|
||||||
|
|
||||||
|
private static readonly MethodInfo methodThenBy = GetExtOrderMethod("ThenBy");
|
||||||
|
|
||||||
|
private static readonly MethodInfo methodThenByDescending = GetExtOrderMethod("ThenByDescending");
|
||||||
|
private static ConcurrentDictionary<Type, Dictionary<string, TypeAcessor>> TypePropSelectors { get; set; } = new();
|
||||||
|
|
||||||
|
private static MethodInfo GetExtOrderMethod(string methodName)
|
||||||
|
=> typeof(System.Linq.Queryable)
|
||||||
|
.GetMethods()
|
||||||
|
.Where(m => m.Name == methodName &&
|
||||||
|
m.IsGenericMethodDefinition &&
|
||||||
|
m.GetParameters().Length == 2 &&
|
||||||
|
m.GetParameters()[1].ParameterType.IsAssignableTo(typeof(LambdaExpression)))
|
||||||
|
.Single();
|
||||||
|
private static Dictionary<string, TypeAcessor> MakeTypeAcessors(Type type)
|
||||||
|
{
|
||||||
|
var propContainer = new Dictionary<string, TypeAcessor>();
|
||||||
|
var properties = type.GetProperties();
|
||||||
|
foreach (var propertyInfo in properties)
|
||||||
|
{
|
||||||
|
var name = propertyInfo.Name.ToLower();
|
||||||
|
ParameterExpression arg = Expression.Parameter(type, "x");
|
||||||
|
MemberExpression property = Expression.Property(arg, propertyInfo.Name);
|
||||||
|
var selector = Expression.Lambda(property, new ParameterExpression[] { arg });
|
||||||
|
var typeAccessor = new TypeAcessor
|
||||||
|
{
|
||||||
|
KeySelector = selector,
|
||||||
|
OrderBy = methodOrderBy.MakeGenericMethod(type, propertyInfo.PropertyType),
|
||||||
|
OrderByDescending = methodOrderByDescending.MakeGenericMethod(type, propertyInfo.PropertyType),
|
||||||
|
ThenBy = methodThenBy.MakeGenericMethod(type, propertyInfo.PropertyType),
|
||||||
|
ThenByDescending = methodThenByDescending.MakeGenericMethod(type, propertyInfo.PropertyType),
|
||||||
|
};
|
||||||
|
|
||||||
|
propContainer.Add(name, typeAccessor);
|
||||||
|
}
|
||||||
|
return propContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить в запрос сортировку по возрастанию или убыванию.
|
||||||
|
/// Этот метод сбросит ранее наложенные сортировки.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="propertySort">
|
||||||
|
/// Свойство сортировки.
|
||||||
|
/// Состоит из названия свойства (в любом регистре)
|
||||||
|
/// и опционально указания направления сортировки "asc" или "desc"
|
||||||
|
/// </param>
|
||||||
|
/// <example>
|
||||||
|
/// var query = query("Date desc");
|
||||||
|
/// </example>
|
||||||
|
/// <returns>Запрос с примененной сортировкой</returns>
|
||||||
|
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||||||
|
this IQueryable<TSource> query,
|
||||||
|
string propertySort)
|
||||||
|
{
|
||||||
|
var parts = propertySort.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var isDesc = parts.Length >= 2 && parts[1].ToLower().Trim() == "desc";
|
||||||
|
var propertyName = parts[0];
|
||||||
|
|
||||||
|
var newQuery = query.SortBy(propertyName, isDesc);
|
||||||
|
return newQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Добавить в запрос сортировку по возрастанию или убыванию
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource"></typeparam>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <param name="propertyName">Название свойства (в любом регистре)</param>
|
||||||
|
/// <param name="isDesc">Сортировать по убыванию</param>
|
||||||
|
/// <returns>Запрос с примененной сортировкой</returns>
|
||||||
|
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||||||
|
this IQueryable<TSource> query,
|
||||||
|
string propertyName,
|
||||||
|
bool isDesc)
|
||||||
|
{
|
||||||
|
var typePropSelector = TypePropSelectors.GetOrAdd(typeof(TSource), MakeTypeAcessors);
|
||||||
|
var propertyNamelower = propertyName.ToLower();
|
||||||
|
var typeAccessor = typePropSelector[propertyNamelower];
|
||||||
|
|
||||||
|
var genericMethod = isDesc
|
||||||
|
? typeAccessor.OrderByDescending
|
||||||
|
: typeAccessor.OrderBy;
|
||||||
|
|
||||||
|
var newQuery = (IOrderedQueryable<TSource>)genericMethod
|
||||||
|
.Invoke(genericMethod, new object[] { query, typeAccessor.KeySelector })!;
|
||||||
|
return newQuery;
|
||||||
|
}
|
||||||
|
}
|
@ -3,40 +3,40 @@ namespace Persistence.Models;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Часть записи описывающая изменение
|
/// Часть записи описывающая изменение
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChangeLogDto<T> where T: class
|
public class ChangeLogDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Запись
|
/// Ключ записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required T Item { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Автор
|
/// Создатель записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UserDto? Author { get; set; }
|
public Guid IdAuthor { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Автор
|
/// Пользователь, изменивший запись
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UserDto? Editor { get; set; }
|
public Guid? IdEditor { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата создания записи
|
/// Дата создания
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTimeOffset Creation { get; set; }
|
public DateTimeOffset Creation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата устаревания (например, при удалении)
|
/// Дата устаревания
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTimeOffset? Obsolete { get; set; }
|
public DateTimeOffset? Obsolete { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Id состояния
|
/// Ключ заменившей записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int IdState { get; set; }
|
public Guid? IdNext { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Id заменяемой записи
|
/// Объект записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? IdPrevious { get; set; }
|
public DataWithWellDepthAndSectionDto Value { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
38
Persistence/Models/DataWithWellDepthAndSectionDto.cs
Normal file
38
Persistence/Models/DataWithWellDepthAndSectionDto.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Persistence.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dto для хранения записей, содержащих начальную и конечную глубину забоя, а также секцию
|
||||||
|
/// </summary>
|
||||||
|
public class DataWithWellDepthAndSectionDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ключ записи
|
||||||
|
/// </summary>
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Глубина забоя на дату начала интервала
|
||||||
|
/// </summary>
|
||||||
|
public double DepthStart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Глубина забоя на дату окончания интервала
|
||||||
|
/// </summary>
|
||||||
|
public double DepthEnd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ключ секции
|
||||||
|
/// </summary>
|
||||||
|
public Guid IdSection { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Объект записи
|
||||||
|
/// </summary>
|
||||||
|
public required IDictionary<string, object> Value { get; set; }
|
||||||
|
}
|
@ -1,62 +0,0 @@
|
|||||||
namespace Persistence.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Часть записи описывающая изменение
|
|
||||||
/// </summary>
|
|
||||||
public interface IChangeLogAbstract
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Актуальная
|
|
||||||
/// </summary>
|
|
||||||
public const int IdStateActual = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Замененная
|
|
||||||
/// </summary>
|
|
||||||
public const int IdStateReplaced = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Удаленная
|
|
||||||
/// </summary>
|
|
||||||
public const int IdStateDeleted = 2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Очищено при импорте
|
|
||||||
/// </summary>
|
|
||||||
public const int IdCleared = 3;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ид записи
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Автор изменения
|
|
||||||
/// </summary>
|
|
||||||
public int IdAuthor { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Редактор
|
|
||||||
/// </summary>
|
|
||||||
public int? IdEditor { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Дата создания записи
|
|
||||||
/// </summary>
|
|
||||||
public DateTimeOffset Creation { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Дата устаревания (например при удалении)
|
|
||||||
/// </summary>
|
|
||||||
public DateTimeOffset? Obsolete { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// "ИД состояния записи: \n0 - актуальная\n1 - замененная\n2 - удаленная
|
|
||||||
/// </summary>
|
|
||||||
public int IdState { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Id заменяемой записи
|
|
||||||
/// </summary>
|
|
||||||
public int? IdPrevious { get; set; }
|
|
||||||
}
|
|
9
Persistence/Models/IWithSectionPart.cs
Normal file
9
Persistence/Models/IWithSectionPart.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Persistence.Models;
|
||||||
|
public interface IWithSectionPart
|
||||||
|
{
|
||||||
|
public double DepthStart { get; set; }
|
||||||
|
|
||||||
|
public double DepthEnd { get; set; }
|
||||||
|
|
||||||
|
public Guid IdSection { get; set; }
|
||||||
|
}
|
@ -1,23 +1,25 @@
|
|||||||
namespace Persistence.Models;
|
namespace Persistence.Models.Requests;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Контейнер для поддержки постраничного просмотра таблиц
|
/// Контейнер для поддержки постраничного просмотра таблиц
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
public class RequestDto
|
public class PaginationRequest
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Кол-во записей пропущенных с начала таблицы в запросе от api
|
/// Кол-во записей пропущенных с начала таблицы в запросе от api
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Skip { get; set; }
|
public int Skip { get; set; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Кол-во записей в запросе от api
|
/// Кол-во записей в запросе от api
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Take { get; set; }
|
public int Take { get; set; } = 32;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Настройки сортировки
|
/// Сортировки:
|
||||||
|
/// Содержат список названий полей сортировки
|
||||||
|
/// Указать направление сортировки можно через пробел "asc" или "desc"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SortSettings { get; set; } = string.Empty;
|
public string? SortSettings { get; set; }
|
||||||
}
|
}
|
22
Persistence/Models/Requests/SectionPartRequest.cs
Normal file
22
Persistence/Models/Requests/SectionPartRequest.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
namespace Persistence.Models.Requests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Запрос для фильтрации данных по секции и глубине
|
||||||
|
/// </summary>
|
||||||
|
public class SectionPartRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Глубина забоя на дату начала интервала
|
||||||
|
/// </summary>
|
||||||
|
public double? DepthStart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Глубина забоя на дату окончания интервала
|
||||||
|
/// </summary>
|
||||||
|
public double? DepthEnd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ключ секции
|
||||||
|
/// </summary>
|
||||||
|
public Guid? IdSection { get; set; }
|
||||||
|
}
|
@ -1,165 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Persistence.Models;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Persistence.Repositories;
|
|
||||||
//public abstract class AbstractChangeLogRepository<TEntity, TChangeLogDto, TDto> : IChangeLogRepository<TDto, TChangeLogDto>
|
|
||||||
// where TDto : class, new()
|
|
||||||
// where TEntity : class, IChangeLogAbstract
|
|
||||||
// where TChangeLogDto : ChangeLogDto<TDto>
|
|
||||||
//{
|
|
||||||
// private readonly DbContext dbContext;
|
|
||||||
|
|
||||||
// protected AbstractChangeLogRepository(DbContext dbContext)
|
|
||||||
// {
|
|
||||||
// this.dbContext = dbContext;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public abstract TEntity Convert(TDto entity);
|
|
||||||
// public async Task<int> Clear(int idUser,CancellationToken token)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
|
|
||||||
// //var updateTime = DateTimeOffset.UtcNow;
|
|
||||||
|
|
||||||
// ////todo
|
|
||||||
// //var query = BuildQuery(request);
|
|
||||||
// //query = query.Where(e => e.Obsolete == null);
|
|
||||||
|
|
||||||
// //var entitiesToDelete = await query.ToArrayAsync(token);
|
|
||||||
|
|
||||||
// //foreach (var entity in entitiesToDelete)
|
|
||||||
// //{
|
|
||||||
// // entity.IdState = IChangeLogAbstract.IdCleared;
|
|
||||||
// // entity.Obsolete = updateTime;
|
|
||||||
// // entity.IdEditor = idUser;
|
|
||||||
// //}
|
|
||||||
|
|
||||||
// //var result = await SaveChangesWithExceptionHandling(token);
|
|
||||||
// //return result;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public async Task<int> ClearAndInsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// var result = 0;
|
|
||||||
// using var transaction = await dbContext.Database.BeginTransactionAsync(token);
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// result += await Clear(idUser, token);
|
|
||||||
// result += await InsertRangeWithoutTransaction(idUser, dtos, token);
|
|
||||||
|
|
||||||
// await transaction.CommitAsync(token);
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
// catch
|
|
||||||
// {
|
|
||||||
// await transaction.RollbackAsync(token);
|
|
||||||
// throw;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public Task<IEnumerable<TDto>> GetCurrent(DateTimeOffset moment, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public Task<IEnumerable<DateOnly>> GetDatesChange(CancellationToken token)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset date, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public async Task<int> AddRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// using var transaction = dbContext.Database.BeginTransaction();
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// var result = await InsertRangeWithoutTransaction(idUser, dtos, token);
|
|
||||||
// await transaction.CommitAsync(token);
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
// catch
|
|
||||||
// {
|
|
||||||
// await transaction.RollbackAsync(token);
|
|
||||||
// throw;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// protected abstract DatabaseFacade GetDataBase();
|
|
||||||
|
|
||||||
// public Task<int> MarkAsDeleted(int idUser, IEnumerable<int> ids, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public Task<int> UpdateOrInsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public Task<int> UpdateRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public Task<IEnumerable<TChangeLogDto>> GetChangeLogForDate(DateTimeOffset? updateFrom, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private async Task<int> InsertRangeWithoutTransaction(int idUser, IEnumerable<TDto> dtos, CancellationToken token)
|
|
||||||
// {
|
|
||||||
// var result = 0;
|
|
||||||
// if (dtos.Any())
|
|
||||||
// {
|
|
||||||
// var entities = dtos.Select(Convert);
|
|
||||||
// var creation = DateTimeOffset.UtcNow;
|
|
||||||
// var dbSet = dbContext.Set<TEntity>();
|
|
||||||
// foreach (var entity in entities)
|
|
||||||
// {
|
|
||||||
// entity.Id = default;
|
|
||||||
// entity.IdAuthor = idUser;
|
|
||||||
// entity.Creation = creation;
|
|
||||||
// entity.IdState = IChangeLogAbstract.IdStateActual;
|
|
||||||
// entity.IdEditor = null;
|
|
||||||
// entity.IdPrevious = null;
|
|
||||||
// entity.Obsolete = null;
|
|
||||||
// dbSet.Add(entity);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// result += await SaveChangesWithExceptionHandling(token);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private async Task<int> SaveChangesWithExceptionHandling(CancellationToken token)
|
|
||||||
// {
|
|
||||||
// var result = await dbContext.SaveChangesAsync(token);
|
|
||||||
// return result;
|
|
||||||
// //try
|
|
||||||
// //{
|
|
||||||
// // var result = await dbContext.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);
|
|
||||||
// //}
|
|
||||||
//}
|
|
@ -1,4 +1,5 @@
|
|||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
|
using Persistence.Models.Requests;
|
||||||
|
|
||||||
namespace Persistence.Repositories;
|
namespace Persistence.Repositories;
|
||||||
|
|
||||||
@ -6,84 +7,81 @@ namespace Persistence.Repositories;
|
|||||||
/// Интерфейс для работы с историческими данными
|
/// Интерфейс для работы с историческими данными
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDto"></typeparam>
|
/// <typeparam name="TDto"></typeparam>
|
||||||
public interface IChangeLogRepository<TDto, TChangeLogDto> : ISyncRepository<TDto>
|
public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<DataWithWellDepthAndSectionDto>
|
||||||
where TDto : class, ITimeSeriesAbstractDto, new()
|
|
||||||
where TChangeLogDto : ChangeLogDto<TDto>
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавление записей
|
/// Добавление записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idUser">пользователь, который добавляет</param>
|
/// <param name="idAuthor">пользователь, который добавляет</param>
|
||||||
|
/// <param name="idDiscriminator">ключ справочника</param>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> InsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token);
|
Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Редактирование записей
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idUser">пользователь, который редактирует</param>
|
|
||||||
/// <param name="dtos"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> UpdateRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавляет Dto у которых id == 0, изменяет dto у которых id != 0
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idUser">пользователь, который редактирует или добавляет</param>
|
|
||||||
/// <param name="dtos"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> UpdateOrInsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Помечает записи как удаленные
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idUser">пользователь, который чистит</param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> Clear(int idUser, CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Очистить и добавить новые
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="idUser"></param>
|
|
||||||
/// <param name="dtos"></param>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> ClearAndInsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Пометить записи как удаленные
|
/// Пометить записи как удаленные
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idUser"></param>
|
/// <param name="idEditor"></param>
|
||||||
/// <param name="ids"></param>
|
/// <param name="ids">ключи записей</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> MarkAsDeleted(int idUser, IEnumerable<int> ids, CancellationToken token);
|
Task<int> MarkAsDeleted(Guid idEditor, IEnumerable<Guid> ids, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение дат изменений записей
|
/// Пометить записи как удаленные
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request"></param>
|
/// <param name="idEditor"></param>
|
||||||
|
/// <param name="idDiscriminator">дискриминатор таблицы</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<DateOnly>> GetDatesChange(CancellationToken token);
|
Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение измененных записей за определенную дату
|
/// Очистить и добавить новые
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="updateFrom"></param>
|
/// <param name="idAuthor"></param>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<TChangeLogDto>> GetChangeLogForDate(DateTimeOffset? updateFrom, CancellationToken token);
|
Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение текущих сейчас записей по параметрам
|
/// Редактирование записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request"></param>
|
/// <param name="idEditor">пользователь, который редактирует</param>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<TDto>> GetCurrent(DateTimeOffset moment, CancellationToken token);
|
Task<int> UpdateRange(Guid idEditor, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение актуальных записей на определенный момент времени (с пагинацией)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="moment">текущий момент времени</param>
|
||||||
|
/// <param name="filterRequest">параметры запроса фильтрации</param>
|
||||||
|
/// <param name="paginationRequest">параметры запроса пагинации</param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<PaginationContainer<DataWithWellDepthAndSectionDto>> GetByDate(Guid idDiscriminator, DateTimeOffset moment, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение измененных записей за период времени
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dateBegin"></param>
|
||||||
|
/// <param name="dateEnd"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,25 @@
|
|||||||
namespace Persistence.Repositories;
|
using Persistence.Models;
|
||||||
|
|
||||||
|
namespace Persistence.Repositories;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Интерфейс по работе с данными
|
/// Интерфейс по работе с данными
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDto"></typeparam>
|
/// <typeparam name="TDto"></typeparam>
|
||||||
public interface ISyncRepository<TDto>
|
public interface ISyncRepository<TDto>
|
||||||
where TDto : class, new()
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить данные, начиная с определенной даты
|
/// Получить данные, начиная с определенной даты
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dateBegin">дата начала</param>
|
/// <param name="dateBegin">дата начала</param>
|
||||||
|
/// <param name="token"></param> /// <returns></returns>
|
||||||
|
Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset dateBegin, CancellationToken token);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||||
|
/// </summary>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset dateBegin, CancellationToken token);
|
Task<DatesRangeDto?> GetDatesRange(CancellationToken token);
|
||||||
}
|
}
|
||||||
|
28
Persistence/Repositories/ISyncWithDiscriminatorRepository.cs
Normal file
28
Persistence/Repositories/ISyncWithDiscriminatorRepository.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using Persistence.Models;
|
||||||
|
|
||||||
|
namespace Persistence.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Интерфейс по работе с данными, у которых есть дискриминатор
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TDto"></typeparam>
|
||||||
|
public interface ISyncWithDiscriminatorRepository<TDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Получить данные, начиная с определенной даты
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">дискриминатор таблицы</param>
|
||||||
|
/// <param name="dateBegin">дата начала</param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// /// <returns></returns>
|
||||||
|
Task<IEnumerable<TDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator">дискриминатор таблицы</param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using Persistence.Models;
|
using Persistence.Models.Requests;
|
||||||
|
|
||||||
namespace Persistence.Repositories;
|
namespace Persistence.Repositories;
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ namespace Persistence.Repositories;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITableDataRepository<TDto, TRequest>
|
public interface ITableDataRepository<TDto, TRequest>
|
||||||
where TDto : class, new()
|
where TDto : class, new()
|
||||||
where TRequest : RequestDto
|
where TRequest : PaginationRequest
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить страницу списка объектов
|
/// Получить страницу списка объектов
|
||||||
|
@ -1,59 +1,59 @@
|
|||||||
using System.Threading.Tasks;
|
using Persistence.Models;
|
||||||
using Persistence.Models;
|
using Persistence.Models.Requests;
|
||||||
|
|
||||||
namespace Persistence.Repositories
|
namespace Persistence.Repositories
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Интерфейс по работе с технологическими сообщениями
|
/// Интерфейс по работе с технологическими сообщениями
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITechMessagesRepository
|
public interface ITechMessagesRepository
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить страницу списка объектов
|
/// Получить страницу списка объектов
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request"></param>
|
/// <param name="request"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<PaginationContainer<TechMessageDto>> GetPage(RequestDto request, CancellationToken token);
|
Task<PaginationContainer<TechMessageDto>> GetPage(PaginationRequest request, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавление новых сообщений
|
/// Добавление новых сообщений
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> AddRange(IEnumerable<TechMessageDto> dtos, Guid userId, CancellationToken token);
|
Task<int> AddRange(IEnumerable<TechMessageDto> dtos, Guid userId, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение списка уникальных названий систем АБ
|
/// Получение списка уникальных названий систем АБ
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<string>> GetSystems(CancellationToken token);
|
Task<IEnumerable<string>> GetSystems(CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение количества сообщений по категориям и системам автобурения
|
/// Получение количества сообщений по категориям и системам автобурения
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="categoryId">Id Категории важности</param>
|
/// <param name="categoryId">Id Категории важности</param>
|
||||||
/// <param name="autoDrillingSystem">Система автобурения</param>
|
/// <param name="autoDrillingSystem">Система автобурения</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<MessagesStatisticDto>> GetStatistics(IEnumerable<string> autoDrillingSystem, IEnumerable<int> categoryIds, CancellationToken token);
|
Task<IEnumerable<MessagesStatisticDto>> GetStatistics(IEnumerable<string> autoDrillingSystem, IEnumerable<int> categoryIds, CancellationToken token);
|
||||||
|
|
||||||
/// <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>
|
||||||
Task<IEnumerable<TechMessageDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token);
|
Task<IEnumerable<TechMessageDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token);
|
Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,4 @@ public interface ITimeSeriesBaseRepository<TDto>
|
|||||||
double intervalSec = 600d,
|
double intervalSec = 600d,
|
||||||
int approxPointsCount = 1024,
|
int approxPointsCount = 1024,
|
||||||
CancellationToken token = default);
|
CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="token"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DatesRangeDto?> GetDatesRange(CancellationToken token);
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user