mirror of
https://github.com/Shchoholiev/shopping-assistant-api.git
synced 2025-04-12 01:48:49 +00:00
made an implementation to start the search and tested it
This commit is contained in:
parent
6ce15d15ae
commit
fc6ce2e6a9
15
ShoppingAssistantApi.Api/Mutations/ProductMutation.cs
Normal file
15
ShoppingAssistantApi.Api/Mutations/ProductMutation.cs
Normal 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);
|
||||||
|
}
|
7
ShoppingAssistantApi.Api/Queries/ProductQuery.cs
Normal file
7
ShoppingAssistantApi.Api/Queries/ProductQuery.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace ShoppingAssistantApi.Api.Queries;
|
||||||
|
|
||||||
|
[ExtendObjectType(OperationTypeNames.Query)]
|
||||||
|
public class ProductQuery
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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]
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user