Setpoint API #1

Merged
on.nemtina merged 10 commits from Setpoint into master 2024-11-25 10:33:36 +05:00
8 changed files with 116 additions and 113 deletions
Showing only changes of commit 9e55d6791c - Show all commits

View File

@ -15,36 +15,37 @@ namespace Persistence.API.Controllers
this.setpointRepository = setpointRepository;
}
[HttpPost("current")]
public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token)
[HttpGet("current")]

Здесь метод [HttpGet]

Здесь метод [HttpGet]
public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetCurrent([FromQuery] IEnumerable<Guid> setpointKeys, CancellationToken token)
{
var result = await setpointRepository.GetCurrent(setpointKeys, token);
return Ok(result);
}
[HttpPost("history")]
public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
[HttpGet("history")]

Здесь метод [HttpGet]

Здесь метод [HttpGet]
public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetHistory([FromQuery] IEnumerable<Guid> setpointKeys, [FromQuery] DateTimeOffset historyMoment, CancellationToken token)
{
var result = await setpointRepository.GetHistory(setpointKeys, historyMoment, token);
return Ok(result);
}
[HttpPost("log")]
public async Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([FromBody] IEnumerable<Guid> setpointKeys, CancellationToken token)
[HttpGet("log")]

Здесь метод [HttpGet]

Здесь метод [HttpGet]
public async Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([FromQuery] IEnumerable<Guid> setpointKeys, CancellationToken token)
{
var result = await setpointRepository.GetLog(setpointKeys, token);
return Ok(result);
}
[HttpPost("save")]
[HttpPost]

Здесь можно просто [HttpPost]

Здесь можно просто [HttpPost]
public async Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token)
{
var result = await setpointRepository.Save(setpointKey, newValue, 0, token);
// ToDo: вычитка idUser

Добавить todo, что необходимо решить вопрос с получением пользователя и авторизацией

Добавить todo, что необходимо решить вопрос с получением пользователя и авторизацией
await setpointRepository.Save(setpointKey, newValue, 0, token);
return Ok(result);
return Ok();
}
}
}

View File

@ -1,6 +1,5 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Persistence.Database.Model
{
@ -13,7 +12,7 @@ namespace Persistence.Database.Model
[Column(TypeName = "jsonb"), Comment("Значение уставки")]
public required object Value { get; set; }
[Comment("Дата изменения уставки")]
[Comment("Дата создания уставки")]
public DateTimeOffset Created { get; set; }
Review

Должно быть "Дата создания уставки"

Должно быть "Дата создания уставки"
[Comment("Id автора последнего изменения")]

View File

@ -1,18 +0,0 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Persistence.Database.Model
{
public class SetpointDictionary
{
[Key, Comment("Ключ")]
public Guid Key { get; set; }
[Comment("Наименование")]
public required string Name { get; set; }
[Comment("Описание")]
public string? Description { get; set; }
}
}

View File

@ -3,18 +3,23 @@ using Refit;
namespace Persistence.IntegrationTests.Clients
{
/// <summary>
/// Интерфейс для тестирования API, предназначенного для работы с уставками
/// </summary>

Заголовки методов запроса должны соответствовать тем, что в контроллере (там часть нужно поменять с post на get)

Заголовки методов запроса должны соответствовать тем, что в контроллере (там часть нужно поменять с post на get)
public interface ISetpointClient
{
[Post("/current")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetCurrent(IEnumerable<Guid> setpointKeys);
private const string BaseRoute = "/api/setpoint";
[Post("/history")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment);
[Get($"{BaseRoute}/current")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetCurrent([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys);
[Post("/log")]
Task<IApiResponse<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog(IEnumerable<Guid> setpoitKeys);
[Get($"{BaseRoute}/history")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetHistory([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys, [Query] DateTimeOffset historyMoment);
[Post("/save")]
Task<IApiResponse<int>> Save(Guid setpointKey, object newValue);
[Get($"{BaseRoute}/log")]
Task<IApiResponse<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys);
[Post($"{BaseRoute}/")]
Task<IApiResponse> Save(Guid setpointKey, object newValue);
}
}

View File

@ -1,6 +1,4 @@
using System.Net;
using System.Text.Json;
using Mapster;
using Persistence.IntegrationTests.Clients;
using Xunit;
@ -16,8 +14,6 @@ namespace Persistence.IntegrationTests.Controllers
}
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
{
factory.ClientOptions.BaseAddress = new Uri($"http://localhost/api/Setpoint");
client = factory.GetHttpClient<ISetpointClient>(string.Empty);
}

Эту строчку лучше убрать, а в интерфейсе ISetpointClient прописать так:

private const string BaseRoute = "/api/setpoint";

 [Post($"{BaseRoute}/current")]
	Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetCurrent(IEnumerable<Guid> setpointKeys);```
Эту строчку лучше убрать, а в интерфейсе ISetpointClient прописать так: ``` private const string BaseRoute = "/api/setpoint"; [Post($"{BaseRoute}/current")] Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetCurrent(IEnumerable<Guid> setpointKeys);```
@ -40,6 +36,22 @@ namespace Persistence.IntegrationTests.Controllers
Assert.Empty(response.Content);
}
[Fact]
public async Task GetCurrent_AfterSave_returns_success()
{
//arrange
var setpointKey = await Save();
//act
var response = await client.GetCurrent([setpointKey]);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.NotEmpty(response.Content);
Assert.Equal(response.Content.FirstOrDefault()?.Key, setpointKey);
}
[Fact]
public async Task GetHistory_returns_success()
{
@ -49,7 +61,7 @@ namespace Persistence.IntegrationTests.Controllers
Guid.NewGuid(),
Guid.NewGuid()
};
var historyMoment = DateTimeOffset.Now.ToUniversalTime();
var historyMoment = DateTimeOffset.UtcNow;
//act
var response = await client.GetHistory(setpointKeys, historyMoment);
@ -60,6 +72,24 @@ namespace Persistence.IntegrationTests.Controllers
Assert.Empty(response.Content);
}
[Fact]
public async Task GetHistory_AfterSave_returns_success()
{
//arrange
var setpointKey = await Save();
var historyMoment = DateTimeOffset.UtcNow;
historyMoment = historyMoment.AddDays(1);
//act
var response = await client.GetHistory([setpointKey], historyMoment);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.NotEmpty(response.Content);
Assert.Equal(response.Content.FirstOrDefault()?.Key, setpointKey);
}
[Fact]
public async Task GetLog_returns_success()
{
@ -79,8 +109,29 @@ namespace Persistence.IntegrationTests.Controllers
Assert.Empty(response.Content);
}
[Fact]
public async Task GetLog_AfterSave_returns_success()
{
//arrange
var setpointKey = await Save();
//act
var response = await client.GetLog([setpointKey]);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.NotEmpty(response.Content);
Assert.Equal(response.Content.FirstOrDefault().Value.FirstOrDefault()?.Key, setpointKey);

Assert.Equal(expected, actual)

Assert.Equal(expected, actual)
}
[Fact]
public async Task Save_returns_success()
{
await Save();
}
private async Task<Guid> Save()
{
//arrange
var setpointKey = Guid.NewGuid();
@ -95,53 +146,8 @@ namespace Persistence.IntegrationTests.Controllers
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(1, response.Content);
}
[Fact]
public async Task General_test_success()
{
//save
var setpointKey = Guid.NewGuid();
var setpointValue = new TestObject()
{
value1 = "1",
value2 = 2
};
var saveResponse = await client.Save(setpointKey, setpointValue);
Assert.Equal(HttpStatusCode.OK, saveResponse.StatusCode);
Assert.Equal(1, saveResponse.Content);
//current
var currentResponse = await client.GetCurrent([setpointKey]);
Assert.Equal(HttpStatusCode.OK, currentResponse.StatusCode);
var currentContent = currentResponse.Content;
Assert.NotNull(currentContent);
Assert.NotEmpty(currentContent);
var currentContentValue = currentContent.FirstOrDefault()?.Value?.ToString();
Assert.NotNull(currentContentValue);
Assert.NotEmpty(currentContentValue);
var testObjectValue = JsonSerializer.Deserialize<TestObject>(currentContentValue);
Assert.NotNull(testObjectValue);
Assert.Equal(setpointValue.value1, testObjectValue.value1);
Assert.Equal(setpointValue.value2, testObjectValue.value2);
//history
var historyMoment = DateTimeOffset.Now.ToUniversalTime();
var historyResponse = await client.GetHistory([setpointKey], historyMoment);
Assert.Equal(HttpStatusCode.OK, historyResponse.StatusCode);
Assert.NotNull(historyResponse.Content);
Assert.NotEmpty(historyResponse.Content);
//log
var logResponse = await client.GetLog([setpointKey]);
Assert.Equal(HttpStatusCode.OK, logResponse.StatusCode);
Assert.NotNull(logResponse.Content);
Assert.NotEmpty(logResponse.Content);
return setpointKey;
}
}
}

View File

@ -1,10 +1,28 @@
namespace Persistence.Repository.Data
{
/// <summary>

Решили, что для всех protected и public классов (а также для интерфейсов) нужно писать комментарии

Решили, что для всех protected и public классов (а также для интерфейсов) нужно писать комментарии
/// Модель для работы с уставкой
/// </summary>
public class SetpointDto
{
/// <summary>
/// Идентификатор уставки
/// </summary>
public int Id { get; set; }
/// <summary>
/// Значение уставки
/// </summary>
public required object Value { get; set; }
/// <summary>
/// Дата сохранения уставки
/// </summary>
public DateTimeOffset Edit { get; set; }
/// <summary>
/// Ключ пользователя
/// </summary>
public int IdUser { get; set; }
}
}

View File

@ -31,9 +31,13 @@ namespace Persistence.Repository.Repositories
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key) && e.Created.Date == historyMoment.Date)
.Where(e => setpointKeys.Contains(e.Key))

Здесь не строгое равенство дат.
Нужно отсортировать уставки по возрастанию дат.
Взять те, у которых Created меньше historyMoment
Из отсортированного и отфильтрованного списка взять последнюю

Здесь не строгое равенство дат. Нужно отсортировать уставки по возрастанию дат. Взять те, у которых Created меньше historyMoment Из отсортированного и отфильтрованного списка взять последнюю
.ToArrayAsync(token);
var dtos = entities.Select(e => e.Adapt<SetpointValueDto>());
var filteredEntities = entities
.GroupBy(e => e.Key)
.Select(e => e.Where(e => e.Created <= historyMoment).Last());

Здесь нужно еще просортировать, чтобы гарантированно взять нужную близлежайшую к historyMoment уставку

Здесь нужно еще просортировать, чтобы гарантированно взять нужную близлежайшую к historyMoment уставку
var dtos = filteredEntities
.Select(e => e.Adapt<SetpointValueDto>());
return dtos;
}
@ -46,35 +50,23 @@ namespace Persistence.Repository.Repositories
.ToArrayAsync(token);
var dtos = entities
.GroupBy(e => e.Key)
.Select(e => new KeyValuePair<Guid, IEnumerable<SetpointLogDto>>(
e.Key,
e.Select(s => s.Adapt<SetpointLogDto>())
)).ToDictionary();
.ToDictionary(e => e.Key, v => v.Select(z => z.Adapt<SetpointLogDto>()));
return dtos;
}
public async Task<int> Save(Guid setpointKey, object newValue, int idUser, CancellationToken token)
{
try
public async Task Save(Guid setpointKey, object newValue, int idUser, CancellationToken token)
{
var entity = new Setpoint()
{
Key = setpointKey,
Value = newValue,
IdUser = idUser,
Created = DateTimeOffset.Now.ToUniversalTime()
Created = DateTimeOffset.UtcNow
};

Можно так: Created = DateTimeOffset.UtcNow

Можно так: Created = DateTimeOffset.UtcNow
await db.Set<Setpoint>().AddAsync(entity, token);
var result = await db.SaveChangesAsync(token);
return result;
}
catch (Exception)
{
return 0;
}
await db.SaveChangesAsync(token);
}
}
}

View File

@ -42,5 +42,5 @@ public interface ISetpointRepository
/// <returns></returns>
/// to do
/// id User учесть в соответствующем методе репозитория
Task<int> Save(Guid setpointKey, object newValue, int idUser, CancellationToken token);
Task Save(Guid setpointKey, object newValue, int idUser, CancellationToken token);
}