Skip to content

Commit

Permalink
Merge branch 'main' into jflaga/functional-test-for-create-order-draf…
Browse files Browse the repository at this point in the history
…t-happy-path

# Conflicts:
#	Directory.Packages.props
#	tests/Ordering.FunctionalTests/OrderingApiTests.cs
  • Loading branch information
jflaga committed Jan 12, 2024
2 parents a91a6a0 + b9af96e commit 47875c6
Show file tree
Hide file tree
Showing 56 changed files with 1,045 additions and 418 deletions.
12 changes: 6 additions & 6 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<MicrosoftExtensionsVersion>8.0.0</MicrosoftExtensionsVersion>
<EfVersion>8.0.0</EfVersion>
<RuntimeVersion>8.0.0</RuntimeVersion>
<AspireVersion>8.0.0-preview.1.23557.2</AspireVersion>
<AspireVersion>8.0.0-preview.2.23619.3</AspireVersion>
<GrpcVersion>2.59.0</GrpcVersion>
</PropertyGroup>
<ItemGroup>
Expand All @@ -31,21 +31,21 @@
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="$(AspnetVersion)" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="$(MicrosoftExtensionsVersion)" />
<!-- Version together with EF -->
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0-rc.2" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="$(EfVersion)" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.15" />
<PackageVersion Include="Pgvector" Version="0.2.0-rc.2" />
<PackageVersion Include="Pgvector.EntityFrameworkCore" Version="0.2.0-rc.1" />
<PackageVersion Include="Pgvector" Version="0.2.0" />
<PackageVersion Include="Pgvector.EntityFrameworkCore" Version="0.2.0" />
<!-- Version together with runtime -->
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(RuntimeVersion)" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(RuntimeVersion)" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(RuntimeVersion)" />
<!-- Xabaril packages -->
<PackageVersion Include="AspNetCore.HealthChecks.Uris" Version="7.0.0" />
<!-- AI -->
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.8" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.0.0-beta8" />
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.12" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.0.1" />
<!-- Open Telemetry -->
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0-alpha.1" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0-alpha.1" />
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@ The sample catalog data is defined in [catalog.json](https://github.com/dotnet/e
### Contributing

For more information on contributing to this repo, please read [the contribution documentation](./CONTRIBUTING.md) and [the Code of Conduct](CODE-OF-CONDUCT.md).

### eShop on Azure

For a version of this app configured for deployment on Azure, please view [the eShop on Azure](https://github.com/Azure-Samples/eShopOnAzure) repo.
2 changes: 1 addition & 1 deletion eShop.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3AF739CD-81D8-428D-A08A-0A58372DEBF6}"
ProjectSection(SolutionItems) = preProject
..\.editorconfig = ..\.editorconfig
.editorconfig = .editorconfig
Directory.Packages.props = Directory.Packages.props
NuGet.config = NuGet.config
README.md = README.md
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"sdk": {
"version": "8.0.100",
"rollForward": "latestPatch",
"rollForward": "major",
"allowPrerelease": true
}
}
7 changes: 7 additions & 0 deletions nuget.config
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,11 @@
<clear />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
</packageSources>

<packageSourceMapping>
<!-- key value for <packageSource> should match key values from <packageSources> element -->
<packageSource key="nuget">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
6 changes: 5 additions & 1 deletion src/Catalog.API/AIOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ public class AIOptions
public class OpenAIOptions
{
/// <summary>OpenAI API key for accessing embedding LLM.</summary>
public string APIKey { get; set; }
public string ApiKey { get; set; }

/// <summary>Optional endpoint for which OpenAI API to access.</summary>
public string Endpoint { get; set; }

/// <summary>The name of the embedding model to use.</summary>
/// <remarks>When using Azure OpenAI, this should be the "Deployment name" of the embedding model.</remarks>
public string EmbeddingName { get; set; }
}
11 changes: 8 additions & 3 deletions src/Catalog.API/Apis/CatalogApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,25 +263,30 @@ public static async Task<Results<Created, NotFound<string>>> UpdateItem(
return TypedResults.Created($"/api/v1/catalog/items/{productToUpdate.Id}");
}

public static async Task<CreatedAtRoute<CatalogItem>> CreateItem(
public static async Task<Created> CreateItem(
[AsParameters] CatalogServices services,
CatalogItem product)
{
var item = new CatalogItem
{
Id = product.Id,
CatalogBrandId = product.CatalogBrandId,
CatalogTypeId = product.CatalogTypeId,
Description = product.Description,
Name = product.Name,
PictureFileName = product.PictureFileName,
Price = product.Price
PictureUri = product.PictureUri,
Price = product.Price,
AvailableStock = product.AvailableStock,
RestockThreshold = product.RestockThreshold,
MaxStockThreshold = product.MaxStockThreshold
};
item.Embedding = await services.CatalogAI.GetEmbeddingAsync(item);

services.Context.CatalogItems.Add(item);
await services.Context.SaveChangesAsync();

return TypedResults.CreatedAtRoute(item, $"/items/{item.Id}");
return TypedResults.Created($"/api/v1/catalog/items/{item.Id}");
}

public static async Task<Results<NoContent, NotFound>> DeleteItemById(
Expand Down
21 changes: 13 additions & 8 deletions src/Catalog.API/Services/CatalogAI.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Azure.AI.OpenAI;
using Azure;
using Azure.AI.OpenAI;
using Pgvector;

namespace eShop.Catalog.API.Services;
Expand All @@ -7,6 +8,8 @@ public sealed class CatalogAI : ICatalogAI
{
/// <summary>OpenAI API key for accessing embedding LLM.</summary>
private readonly string _aiKey;
/// <summary>Optional OpenAI API endpoint.</summary>
private readonly string _aiEndpoint;
/// <summary>The name of the embedding model to use.</summary>
private readonly string _aiEmbeddingModel;

Expand All @@ -19,7 +22,8 @@ public CatalogAI(IOptions<AIOptions> options, IWebHostEnvironment environment, I
{
var aiOptions = options.Value;

_aiKey = aiOptions.OpenAI.APIKey;
_aiKey = aiOptions.OpenAI.ApiKey;
_aiEndpoint = aiOptions.OpenAI.Endpoint;
_aiEmbeddingModel = aiOptions.OpenAI.EmbeddingName ?? "text-embedding-ada-002";
IsEnabled = !string.IsNullOrWhiteSpace(_aiKey);

Expand Down Expand Up @@ -49,18 +53,19 @@ public async ValueTask<Vector> GetEmbeddingAsync(string text)
_logger.LogInformation("Getting embedding for \"{text}\"", text);
}

EmbeddingsOptions options = new(text);
var result = (await GetAIClient().GetEmbeddingsAsync(_aiEmbeddingModel, options)).Value.Data[0].Embedding;
return new Vector(result as float[] ?? [.. result]);
EmbeddingsOptions options = new(_aiEmbeddingModel, [text]);
return new Vector((await GetAIClient().GetEmbeddingsAsync(options)).Value.Data[0].Embedding);
}

/// <summary>Gets an embedding vector for the specified catalog item.</summary>
public ValueTask<Vector> GetEmbeddingAsync(CatalogItem item) => IsEnabled ?
public ValueTask<Vector> GetEmbeddingAsync(CatalogItem item) => IsEnabled ?
GetEmbeddingAsync($"{item.Name} {item.Description}") :
ValueTask.FromResult<Vector>(null);

/// <summary>Gets the AI client used for creating embeddings.</summary>
private OpenAIClient GetAIClient() => !string.IsNullOrWhiteSpace(_aiKey) ?
new OpenAIClient(_aiKey) :
private OpenAIClient GetAIClient() => !string.IsNullOrWhiteSpace(_aiKey) ?
!string.IsNullOrWhiteSpace(_aiEndpoint) ?
new OpenAIClient(new Uri(_aiEndpoint), new AzureKeyCredential(_aiKey)) :
new OpenAIClient(_aiKey) :
throw new InvalidOperationException("AI API key not configured");
}
2 changes: 1 addition & 1 deletion src/ClientApp/Resources/Styles/Styles.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@

<OnPlatform x:Key="MidMediumSize" x:TypeArguments="x:Double">
<On Platform="iOS" Value="12" />
<On Platform="DefaultI" Value="14" />
<On Platform="Default" Value="14" />
</OnPlatform>

<OnPlatform x:Key="MediumSize" x:TypeArguments="x:Double">
Expand Down
2 changes: 1 addition & 1 deletion src/OrderProcessor/Services/GracePeriodManagerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private async ValueTask<List<int>> GetConfirmedGracePeriodOrders()
command.CommandText = """
SELECT "Id"
FROM ordering.orders
WHERE CURRENT_TIMESTAMP - "OrderDate" >= @GracePeriodTime AND "OrderStatusId" = 1
WHERE CURRENT_TIMESTAMP - "OrderDate" >= @GracePeriodTime AND "OrderStatus" = 'Submitted'
""";
command.Parameters.AddWithValue("GracePeriodTime", TimeSpan.FromMinutes(_options.GracePeriodTime));

Expand Down
7 changes: 2 additions & 5 deletions src/Ordering.API/Application/Commands/CancelOrderCommand.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
namespace eShop.Ordering.API.Application.Commands;

public class CancelOrderCommand(int orderNumber) : IRequest<bool>
{
[DataMember]
public int OrderNumber { get; set; } = orderNumber;
}
public record CancelOrderCommand(int OrderNumber) : IRequest<bool>;

13 changes: 1 addition & 12 deletions src/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
namespace eShop.Ordering.API.Application.Commands;
using eShop.Ordering.API.Application.Models;

public class CreateOrderDraftCommand : IRequest<OrderDraftDTO>
{
public string BuyerId { get; private set; }

public IEnumerable<BasketItem> Items { get; private set; }

public CreateOrderDraftCommand(string buyerId, IEnumerable<BasketItem> items)
{
BuyerId = buyerId;
Items = items;
}
}
public record CreateOrderDraftCommand(string BuyerId, IEnumerable<BasketItem> Items) : IRequest<OrderDraftDTO>;
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
namespace eShop.Ordering.API.Application.Commands;

public class SetAwaitingValidationOrderStatusCommand : IRequest<bool>
{

[DataMember]
public int OrderNumber { get; private set; }

public SetAwaitingValidationOrderStatusCommand(int orderNumber)
{
OrderNumber = orderNumber;
}
}
public record SetAwaitingValidationOrderStatusCommand(int OrderNumber) : IRequest<bool>;
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
namespace eShop.Ordering.API.Application.Commands;

public class SetPaidOrderStatusCommand : IRequest<bool>
{

[DataMember]
public int OrderNumber { get; private set; }

public SetPaidOrderStatusCommand(int orderNumber)
{
OrderNumber = orderNumber;
}
}
public record SetPaidOrderStatusCommand(int OrderNumber) : IRequest<bool>;
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
namespace eShop.Ordering.API.Application.Commands;

public class SetStockConfirmedOrderStatusCommand : IRequest<bool>
{

[DataMember]
public int OrderNumber { get; private set; }

public SetStockConfirmedOrderStatusCommand(int orderNumber)
{
OrderNumber = orderNumber;
}
}
public record SetStockConfirmedOrderStatusCommand(int OrderNumber) : IRequest<bool>;
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
namespace eShop.Ordering.API.Application.Commands;

public class SetStockRejectedOrderStatusCommand : IRequest<bool>
{

[DataMember]
public int OrderNumber { get; private set; }

[DataMember]
public List<int> OrderStockItems { get; private set; }

public SetStockRejectedOrderStatusCommand(int orderNumber, List<int> orderStockItems)
{
OrderNumber = orderNumber;
OrderStockItems = orderStockItems;
}
}
public record SetStockRejectedOrderStatusCommand(int OrderNumber, List<int> OrderStockItems) : IRequest<bool>;
12 changes: 1 addition & 11 deletions src/Ordering.API/Application/Commands/ShipOrderCommand.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
namespace eShop.Ordering.API.Application.Commands;

public class ShipOrderCommand : IRequest<bool>
{

[DataMember]
public int OrderNumber { get; private set; }

public ShipOrderCommand(int orderNumber)
{
OrderNumber = orderNumber;
}
}
public record ShipOrderCommand(int OrderNumber) : IRequest<bool>;
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public OrderCancelledDomainEventHandler(

public async Task Handle(OrderCancelledDomainEvent domainEvent, CancellationToken cancellationToken)
{
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.Order.Id, nameof(OrderStatus.Cancelled), OrderStatus.Cancelled.Id);
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.Order.Id, OrderStatus.Cancelled);

var order = await _orderRepository.GetAsync(domainEvent.Order.Id);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value);

var integrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name, buyer.IdentityGuid);
var integrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus, buyer.Name, buyer.IdentityGuid);
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public OrderShippedDomainEventHandler(

public async Task Handle(OrderShippedDomainEvent domainEvent, CancellationToken cancellationToken)
{
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.Order.Id, nameof(OrderStatus.Shipped), OrderStatus.Shipped.Id);
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.Order.Id, OrderStatus.Shipped);

var order = await _orderRepository.GetAsync(domainEvent.Order.Id);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value);

var integrationEvent = new OrderStatusChangedToShippedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name, buyer.IdentityGuid);
var integrationEvent = new OrderStatusChangedToShippedIntegrationEvent(order.Id, order.OrderStatus, buyer.Name, buyer.IdentityGuid);
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ public OrderStatusChangedToAwaitingValidationDomainEventHandler(

public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent domainEvent, CancellationToken cancellationToken)
{
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.OrderId, nameof(OrderStatus.AwaitingValidation), OrderStatus.AwaitingValidation.Id);
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.OrderId, OrderStatus.AwaitingValidation);

var order = await _orderRepository.GetAsync(domainEvent.OrderId);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value);

var orderStockList = domainEvent.OrderItems
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));

var integrationEvent = new OrderStatusChangedToAwaitingValidationIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name, buyer.IdentityGuid, orderStockList);
var integrationEvent = new OrderStatusChangedToAwaitingValidationIntegrationEvent(order.Id, order.OrderStatus, buyer.Name, buyer.IdentityGuid, orderStockList);
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public OrderStatusChangedToPaidDomainEventHandler(

public async Task Handle(OrderStatusChangedToPaidDomainEvent domainEvent, CancellationToken cancellationToken)
{
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.OrderId, nameof(OrderStatus.Paid), OrderStatus.Paid.Id);
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.OrderId, OrderStatus.Paid);

var order = await _orderRepository.GetAsync(domainEvent.OrderId);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value);
Expand All @@ -31,7 +31,7 @@ public async Task Handle(OrderStatusChangedToPaidDomainEvent domainEvent, Cancel

var integrationEvent = new OrderStatusChangedToPaidIntegrationEvent(
domainEvent.OrderId,
order.OrderStatus.Name,
order.OrderStatus,
buyer.Name,
buyer.IdentityGuid,
orderStockList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public OrderStatusChangedToStockConfirmedDomainEventHandler(

public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent domainEvent, CancellationToken cancellationToken)
{
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.OrderId, nameof(OrderStatus.StockConfirmed), OrderStatus.StockConfirmed.Id);
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.OrderId, OrderStatus.StockConfirmed);

var order = await _orderRepository.GetAsync(domainEvent.OrderId);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value);

var integrationEvent = new OrderStatusChangedToStockConfirmedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name, buyer.IdentityGuid);
var integrationEvent = new OrderStatusChangedToStockConfirmedIntegrationEvent(order.Id, order.OrderStatus, buyer.Name, buyer.IdentityGuid);
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public async Task Handle(OrderStartedDomainEvent domainEvent, CancellationToken
await _buyerRepository.UnitOfWork
.SaveEntitiesAsync(cancellationToken);

var integrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(domainEvent.Order.Id, domainEvent.Order.OrderStatus.Name, buyer.Name, buyer.IdentityGuid);
var integrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(domainEvent.Order.Id, domainEvent.Order.OrderStatus, buyer.Name, buyer.IdentityGuid);
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent);
OrderingApiTrace.LogOrderBuyerAndPaymentValidatedOrUpdated(_logger, buyer.Id, domainEvent.Order.Id);
}
Expand Down
Loading

0 comments on commit 47875c6

Please sign in to comment.