made an implementation to start the search and tested it

This commit is contained in:
stasex 2023-10-20 23:30:09 +03:00
parent 6ce15d15ae
commit fc6ce2e6a9
6 changed files with 92 additions and 54 deletions

View File

@ -0,0 +1,15 @@
using ShoppingAssistantApi.Application.IServices;
using ShoppingAssistantApi.Application.Models.Dtos;
using ShoppingAssistantApi.Application.Models.ProductSearch;
using ShoppingAssistantApi.Domain.Entities;
using ShoppingAssistantApi.Infrastructure.Services;
namespace ShoppingAssistantApi.Api.Mutations;
[ExtendObjectType(OperationTypeNames.Mutation)]
public class ProductMutation
{
public IAsyncEnumerable<(List<ProductName> ProductNames, WishlistDto Wishlist)> StartNewSearchAndReturnWishlist(
Message message, CancellationToken cancellationToken, [Service] IProductService productService)
=> productService.StartNewSearchAndReturnWishlist(message, cancellationToken);
}

View File

@ -0,0 +1,7 @@
namespace ShoppingAssistantApi.Api.Queries;
[ExtendObjectType(OperationTypeNames.Query)]
public class ProductQuery
{
}

View File

@ -1,12 +1,14 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using ShoppingAssistantApi.Application.Models.Dtos;
using ShoppingAssistantApi.Application.Models.OpenAi; using ShoppingAssistantApi.Application.Models.OpenAi;
using ShoppingAssistantApi.Application.Models.ProductSearch;
using ShoppingAssistantApi.Domain.Entities; using ShoppingAssistantApi.Domain.Entities;
namespace ShoppingAssistantApi.Application.IServices; namespace ShoppingAssistantApi.Application.IServices;
public interface IProductService public interface IProductService
{ {
Task<List<string>> StartNewSearchAndReturnWishlist(Message message, CancellationToken cancellationToken); IAsyncEnumerable<(List<ProductName> ProductNames, WishlistDto Wishlist)> StartNewSearchAndReturnWishlist(Message message, CancellationToken cancellationToken);
Task<List<string>> GetProductFromSearch(Message message, CancellationToken cancellationToken); Task<List<string>> GetProductFromSearch(Message message, CancellationToken cancellationToken);

View File

@ -1,9 +1,11 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using ShoppingAssistantApi.Application.IRepositories; using ShoppingAssistantApi.Application.IRepositories;
using ShoppingAssistantApi.Application.IServices; using ShoppingAssistantApi.Application.IServices;
using ShoppingAssistantApi.Application.Models.CreateDtos; using ShoppingAssistantApi.Application.Models.CreateDtos;
using ShoppingAssistantApi.Application.Models.Dtos;
using ShoppingAssistantApi.Application.Models.OpenAi; using ShoppingAssistantApi.Application.Models.OpenAi;
using ShoppingAssistantApi.Application.Models.ProductSearch; using ShoppingAssistantApi.Application.Models.ProductSearch;
using ShoppingAssistantApi.Domain.Entities; using ShoppingAssistantApi.Domain.Entities;
@ -13,21 +15,17 @@ namespace ShoppingAssistantApi.Infrastructure.Services;
public class ProductService : IProductService public class ProductService : IProductService
{ {
private readonly IWishlistsRepository _wishlistsRepository;
private readonly IWishlistsService _wishlistsService; private readonly IWishlistsService _wishlistsService;
private readonly IOpenAiService _openAiService; private readonly IOpenAiService _openAiService;
public ProductService(IOpenAiService openAiService, IWishlistsService wishlistsService, IWishlistsRepository wishlistsRepository) public ProductService(IOpenAiService openAiService, IWishlistsService wishlistsService)
{ {
_openAiService = openAiService; _openAiService = openAiService;
_wishlistsService = wishlistsService; _wishlistsService = wishlistsService;
_wishlistsRepository = wishlistsRepository;
} }
public async Task<List<string>> StartNewSearchAndReturnWishlist(Message message, public async IAsyncEnumerable<(List<ProductName> ProductNames, WishlistDto Wishlist)> StartNewSearchAndReturnWishlist(Message message, CancellationToken cancellationToken)
CancellationToken cancellationToken)
{ {
List<OpenAiMessage> messages = new List<OpenAiMessage>() List<OpenAiMessage> messages = new List<OpenAiMessage>()
{ {
@ -37,27 +35,28 @@ public class ProductService : IProductService
Content = PromptForProductSearch(message.Text) Content = PromptForProductSearch(message.Text)
} }
}; };
var chatRequest = new ChatCompletionRequest var chatRequest = new ChatCompletionRequest
{ {
Messages = messages Messages = messages
}; };
var openAiMessage = await _openAiService.GetChatCompletion(chatRequest, cancellationToken); await foreach (var response in _openAiService.GetChatCompletionStream(chatRequest, cancellationToken))
var openAiContent = JObject.Parse(openAiMessage.Content);
var productNames = openAiContent["Name"]?.ToObject<List<ProductName>>() ?? new List<ProductName>();
WishlistCreateDto newWishlist = new WishlistCreateDto()
{ {
Type = "Product", var openAiContent = JObject.Parse(response);
FirstMessageText = message.Text var productNames = openAiContent["Name"]?.ToObject<List<ProductName>>() ?? new List<ProductName>();
};
var resultWishList = _wishlistsService.StartPersonalWishlistAsync(newWishlist, cancellationToken); WishlistCreateDto newWishlist = new WishlistCreateDto()
{
return productNames.Select(productName => productName.Name).ToList(); Type = "Product",
FirstMessageText = message.Text
};
var resultWishlistTask = _wishlistsService.StartPersonalWishlistAsync(newWishlist, cancellationToken);
var resultWishlist = await resultWishlistTask;
yield return (productNames, resultWishlist);
}
} }
public async Task<List<string>> GetProductFromSearch(Message message, CancellationToken cancellationToken) public async Task<List<string>> GetProductFromSearch(Message message, CancellationToken cancellationToken)

View File

@ -1,25 +1,31 @@
using MongoDB.Bson; using Microsoft.Extensions.DependencyInjection;
using MongoDB.Bson;
using Moq; using Moq;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using ShoppingAssistantApi.Application.IServices; using ShoppingAssistantApi.Application.IServices;
using ShoppingAssistantApi.Application.Models.CreateDtos;
using ShoppingAssistantApi.Application.Models.Dtos;
using ShoppingAssistantApi.Application.Models.OpenAi; using ShoppingAssistantApi.Application.Models.OpenAi;
using ShoppingAssistantApi.Application.Models.ProductSearch; using ShoppingAssistantApi.Application.Models.ProductSearch;
using ShoppingAssistantApi.Domain.Entities; using ShoppingAssistantApi.Domain.Entities;
using ShoppingAssistantApi.Domain.Enums; using ShoppingAssistantApi.Domain.Enums;
using ShoppingAssistantApi.Tests.TestExtentions; using ShoppingAssistantApi.Infrastructure.Services;
namespace ShoppingAssistantApi.Tests.Tests; namespace ShoppingAssistantApi.Tests.Tests;
public class ProductTests : TestsBase public class ProductTests
{ {
private Mock<IOpenAiService> _openAiServiceMock; private Mock<IOpenAiService> _openAiServiceMock;
private Mock<IProductService> _productServiceMock; private Mock<IProductService> _productServiceMock;
public ProductTests(TestingFactory<Program> factory) : base(factory) public Mock<IWishlistsService> _wishListServiceMock;
public ProductTests()
{ {
_openAiServiceMock = new Mock<IOpenAiService>(); _openAiServiceMock = new Mock<IOpenAiService>();
_productServiceMock = new Mock<IProductService>(); _productServiceMock = new Mock<IProductService>();
_wishListServiceMock = new Mock<IWishlistsService>();
} }
[Fact] [Fact]
@ -33,42 +39,50 @@ public class ProductTests : TestsBase
Role = "user" Role = "user"
}; };
var cancellationToken = CancellationToken.None; var cancellationToken = CancellationToken.None;
var expectedProductList = new List<string> { "NVIDIA GeForce RTX 3080", "AMD Radeon RX 6900 XT" };
_productServiceMock.Setup(x => x.GetProductFromSearch(message, cancellationToken))
.ReturnsAsync(expectedProductList);
Wishlist createdWishList = null;
var expectedOpenAiMessage = new OpenAiMessage var expectedOpenAiMessage = new OpenAiMessage
{ {
Role = OpenAiRole.User, Role = OpenAiRole.User,
Content = "{ \"Name\": [{ \"Name\": \"NVIDIA GeForce RTX 3080\" }, { \"Name\": \"AMD Radeon RX 6900 XT\" }] }" Content = "{ \"Name\": [{ \"Name\": \"NVIDIA GeForce RTX 3080\" }, { \"Name\": \"AMD Radeon RX 6900 XT\" }] }"
}; };
_openAiServiceMock.Setup(x => x.GetChatCompletion(It.IsAny<ChatCompletionRequest>(), cancellationToken))
.ReturnsAsync(expectedOpenAiMessage); var openAiServiceMock = new Mock<IOpenAiService>();
_productServiceMock var wishlistsServiceMock = new Mock<IWishlistsService>();
.Setup(x => x.StartNewSearchAndReturnWishlist(It.IsAny<Message>(), cancellationToken))
.ReturnsAsync(() => openAiServiceMock.Setup(x => x.GetChatCompletionStream(It.IsAny<ChatCompletionRequest>(), cancellationToken))
.Returns((ChatCompletionRequest request, CancellationToken token) =>
{ {
createdWishList = new Wishlist var asyncEnumerable = new List<string> { expectedOpenAiMessage.Content }.ToAsyncEnumerable();
{ return asyncEnumerable;
Name = "Test Wishlist",
CreatedById = ObjectId.GenerateNewId(),
Id = ObjectId.GenerateNewId(),
Type = "Test Type"
};
return new List<string>();
}); });
await _productServiceMock.Object.StartNewSearchAndReturnWishlist(message, cancellationToken); wishlistsServiceMock.Setup(x => x.StartPersonalWishlistAsync(It.IsAny<WishlistCreateDto>(), cancellationToken))
.ReturnsAsync(new WishlistDto
var openAiContent = JObject.Parse(expectedOpenAiMessage.Content); {
var productNames = openAiContent["Name"].ToObject<List<ProductName>>(); Id = "someID",
var productList = productNames.Select(info => info.Name).ToList(); Name = "MacBook",
Type = "Product",
CreatedById = "someId"
});
var productService = new ProductService(openAiServiceMock.Object, wishlistsServiceMock.Object);
List<ProductName> productNames = null;
WishlistDto createdWishList = null;
var result = productService.StartNewSearchAndReturnWishlist(message, cancellationToken);
await foreach (var (productList, wishlist) in result)
{
productNames = productList;
createdWishList = wishlist;
}
var openAiContent = JObject.Parse(expectedOpenAiMessage.Content);
Assert.Equal(expectedProductList, productList);
Assert.True(openAiContent.ContainsKey("Name")); Assert.True(openAiContent.ContainsKey("Name"));
Assert.NotNull(createdWishList); Assert.NotNull(createdWishList);
Assert.NotNull(productNames);
} }
[Fact] [Fact]

View File

@ -12,6 +12,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="Moq" Version="4.20.69" /> <PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="xunit" Version="2.4.2" /> <PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -23,9 +24,9 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ShoppingAssistantApi.Infrastructure\ShoppingAssistantApi.Infrastructure.csproj" /> <ProjectReference Include="..\ShoppingAssistantApi.Infrastructure\ShoppingAssistantApi.Infrastructure.csproj" />
<ProjectReference Include="..\ShoppingAssistantApi.Application\ShoppingAssistantApi.Application.csproj" /> <ProjectReference Include="..\ShoppingAssistantApi.Application\ShoppingAssistantApi.Application.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>