Slimmed down to Ordering Service
This commit is contained in:
parent
ad7ff76fed
commit
4297b2efe0
19
src/.env
19
src/.env
@ -4,32 +4,13 @@
|
|||||||
|
|
||||||
# The IP below should be swapped to your real IP or DNS name, like 192.168.88.248, etc. if testing from remote browsers or mobile devices
|
# The IP below should be swapped to your real IP or DNS name, like 192.168.88.248, etc. if testing from remote browsers or mobile devices
|
||||||
|
|
||||||
# Use this values to run the app locally in Windows
|
|
||||||
ESHOP_EXTERNAL_DNS_NAME_OR_IP=host.docker.internal
|
|
||||||
ESHOP_STORAGE_CATALOG_URL=http://host.docker.internal:5202/c/api/v1/catalog/items/[0]/pic/
|
|
||||||
|
|
||||||
# Use this values to run the app locally in Mac
|
|
||||||
# ESHOP_EXTERNAL_DNS_NAME_OR_IP=docker.for.mac.localhost
|
|
||||||
# ESHOP_STORAGE_CATALOG_URL=http://docker.for.mac.localhost:5202/c/api/v1/catalog/items/[0]/pic/
|
|
||||||
|
|
||||||
# Use this values to run the app locally in Linux
|
|
||||||
# ESHOP_EXTERNAL_DNS_NAME_OR_IP=docker.for.linux.localhost
|
|
||||||
# ESHOP_STORAGE_CATALOG_URL=http://docker.for.linux.localhost:5202/c/api/v1/catalog/items/[0]/pic/
|
|
||||||
|
|
||||||
# Configure this values to the cloud storage locations
|
|
||||||
# ESHOP_STORAGE_CATALOG_URL=<YourAzureStorage_Catalog_BLOB_URL>
|
|
||||||
|
|
||||||
ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=10.121.122.162
|
ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=10.121.122.162
|
||||||
|
|
||||||
#ESHOP_AZURE_REDIS_BASKET_DB=<YourAzureRedisBasketInfo>
|
#ESHOP_AZURE_REDIS_BASKET_DB=<YourAzureRedisBasketInfo>
|
||||||
#ESHOP_AZURE_SERVICE_BUS=<YourAzureServiceBusInfo>
|
#ESHOP_AZURE_SERVICE_BUS=<YourAzureServiceBusInfo>
|
||||||
#ESHOP_AZURE_COSMOSDB=<YourAzureCosmosDBConnData>
|
#ESHOP_AZURE_COSMOSDB=<YourAzureCosmosDBConnData>
|
||||||
#ESHOP_AZURE_CATALOG_DB=<YourAzureSQLDBCatalogDBConnString>
|
|
||||||
#ESHOP_AZURE_IDENTITY_DB=<YourAzureSQLDBIdentityDBConnString>
|
|
||||||
#ESHOP_AZURE_ORDERING_DB=<YourAzureSQLDBOrderingDBConnString>
|
#ESHOP_AZURE_ORDERING_DB=<YourAzureSQLDBOrderingDBConnString>
|
||||||
#ESHOP_AZUREFUNC_CAMPAIGN_DETAILS_URI=<YourAzureFunctionCampaignDetailsURI>
|
#ESHOP_AZUREFUNC_CAMPAIGN_DETAILS_URI=<YourAzureFunctionCampaignDetailsURI>
|
||||||
#ESHOP_AZURE_STORAGE_CATALOG_NAME=<YourAzureStorageCatalogName>
|
|
||||||
#ESHOP_AZURE_STORAGE_CATALOG_KEY=<YourAzureStorageCatalogKey>
|
|
||||||
#ESHOP_SERVICE_BUS_USERNAME=<ServiceBusUserName-OnlyUsedIfUsingRabbitMQUnderwindows>
|
#ESHOP_SERVICE_BUS_USERNAME=<ServiceBusUserName-OnlyUsedIfUsingRabbitMQUnderwindows>
|
||||||
#ESHOP_SERVICE_BUS_PASSWORD=<ServiceBusUserPassword-OnlyUsedIfUsingRabbitMQUnderwindows>
|
#ESHOP_SERVICE_BUS_PASSWORD=<ServiceBusUserPassword-OnlyUsedIfUsingRabbitMQUnderwindows>
|
||||||
#INSTRUMENTATION_KEY=
|
#INSTRUMENTATION_KEY=
|
@ -1,139 +0,0 @@
|
|||||||
admin:
|
|
||||||
access_log_path: "/dev/null"
|
|
||||||
address:
|
|
||||||
socket_address:
|
|
||||||
address: 0.0.0.0
|
|
||||||
port_value: 8001
|
|
||||||
static_resources:
|
|
||||||
listeners:
|
|
||||||
- address:
|
|
||||||
socket_address:
|
|
||||||
address: 0.0.0.0
|
|
||||||
port_value: 80
|
|
||||||
filter_chains:
|
|
||||||
- filters:
|
|
||||||
- name: envoy.http_connection_manager
|
|
||||||
config:
|
|
||||||
codec_type: auto
|
|
||||||
stat_prefix: ingress_http
|
|
||||||
route_config:
|
|
||||||
name: eshop_backend_route
|
|
||||||
virtual_hosts:
|
|
||||||
- name: eshop_backend
|
|
||||||
domains:
|
|
||||||
- "*"
|
|
||||||
routes:
|
|
||||||
- name: "c-short"
|
|
||||||
match:
|
|
||||||
prefix: "/c/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
prefix_rewrite: "/catalog-api/"
|
|
||||||
cluster: catalog
|
|
||||||
- name: "c-long"
|
|
||||||
match:
|
|
||||||
prefix: "/catalog-api/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
cluster: catalog
|
|
||||||
- name: "o-short"
|
|
||||||
match:
|
|
||||||
prefix: "/o/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
prefix_rewrite: "/ordering-api/"
|
|
||||||
cluster: ordering
|
|
||||||
- name: "o-long"
|
|
||||||
match:
|
|
||||||
prefix: "/ordering-api/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
cluster: ordering
|
|
||||||
- name: "h-long"
|
|
||||||
match:
|
|
||||||
prefix: "/hub/notificationhub"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
cluster: signalr-hub
|
|
||||||
timeout: 300s
|
|
||||||
- name: "b-short"
|
|
||||||
match:
|
|
||||||
prefix: "/b/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
prefix_rewrite: "/basket-api/"
|
|
||||||
cluster: basket
|
|
||||||
- name: "b-long"
|
|
||||||
match:
|
|
||||||
prefix: "/basket-api/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
cluster: basket
|
|
||||||
- name: "agg"
|
|
||||||
match:
|
|
||||||
prefix: "/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
prefix_rewrite: "/"
|
|
||||||
cluster: shoppingagg
|
|
||||||
http_filters:
|
|
||||||
- name: envoy.router
|
|
||||||
access_log:
|
|
||||||
- name: envoy.file_access_log
|
|
||||||
filter:
|
|
||||||
not_health_check_filter: {}
|
|
||||||
config:
|
|
||||||
json_format:
|
|
||||||
time: "%START_TIME%"
|
|
||||||
protocol: "%PROTOCOL%"
|
|
||||||
duration: "%DURATION%"
|
|
||||||
request_method: "%REQ(:METHOD)%"
|
|
||||||
request_host: "%REQ(HOST)%"
|
|
||||||
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
|
|
||||||
response_flags: "%RESPONSE_FLAGS%"
|
|
||||||
route_name: "%ROUTE_NAME%"
|
|
||||||
upstream_host: "%UPSTREAM_HOST%"
|
|
||||||
upstream_cluster: "%UPSTREAM_CLUSTER%"
|
|
||||||
upstream_local_address: "%UPSTREAM_LOCAL_ADDRESS%"
|
|
||||||
path: "/tmp/access.log"
|
|
||||||
clusters:
|
|
||||||
- name: shoppingagg
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: mobileshoppingagg
|
|
||||||
port_value: 80
|
|
||||||
- name: catalog
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: catalog-api
|
|
||||||
port_value: 80
|
|
||||||
- name: basket
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: basket-api
|
|
||||||
port_value: 80
|
|
||||||
- name: ordering
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: ordering-api
|
|
||||||
port_value: 80
|
|
||||||
- name: signalr-hub
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: ordering-signalrhub
|
|
||||||
port_value: 80
|
|
@ -1,142 +0,0 @@
|
|||||||
admin:
|
|
||||||
access_log_path: "/dev/null"
|
|
||||||
address:
|
|
||||||
socket_address:
|
|
||||||
address: 0.0.0.0
|
|
||||||
port_value: 8001
|
|
||||||
static_resources:
|
|
||||||
listeners:
|
|
||||||
- address:
|
|
||||||
socket_address:
|
|
||||||
address: 0.0.0.0
|
|
||||||
port_value: 80
|
|
||||||
filter_chains:
|
|
||||||
- filters:
|
|
||||||
- name: envoy.http_connection_manager
|
|
||||||
config:
|
|
||||||
codec_type: auto
|
|
||||||
stat_prefix: ingress_http
|
|
||||||
route_config:
|
|
||||||
name: eshop_backend_route
|
|
||||||
virtual_hosts:
|
|
||||||
- name: eshop_backend
|
|
||||||
domains:
|
|
||||||
- "*"
|
|
||||||
routes:
|
|
||||||
- name: "c-short"
|
|
||||||
match:
|
|
||||||
prefix: "/c/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
prefix_rewrite: "/catalog-api/"
|
|
||||||
cluster: catalog
|
|
||||||
- name: "c-long"
|
|
||||||
match:
|
|
||||||
prefix: "/catalog-api/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
cluster: catalog
|
|
||||||
- name: "o-short"
|
|
||||||
match:
|
|
||||||
prefix: "/o/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
prefix_rewrite: "/ordering-api/"
|
|
||||||
cluster: ordering
|
|
||||||
- name: "o-long"
|
|
||||||
match:
|
|
||||||
prefix: "/ordering-api/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
cluster: ordering
|
|
||||||
- name: "h-long"
|
|
||||||
match:
|
|
||||||
prefix: "/hub/notificationhub"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
cluster: signalr-hub
|
|
||||||
timeout: 300s
|
|
||||||
upgrade_configs:
|
|
||||||
upgrade_type: "websocket"
|
|
||||||
enabled: true
|
|
||||||
- name: "b-short"
|
|
||||||
match:
|
|
||||||
prefix: "/b/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
prefix_rewrite: "/basket-api/"
|
|
||||||
cluster: basket
|
|
||||||
- name: "b-long"
|
|
||||||
match:
|
|
||||||
prefix: "/basket-api/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
cluster: basket
|
|
||||||
- name: "agg"
|
|
||||||
match:
|
|
||||||
prefix: "/"
|
|
||||||
route:
|
|
||||||
auto_host_rewrite: true
|
|
||||||
prefix_rewrite: "/"
|
|
||||||
cluster: shoppingagg
|
|
||||||
http_filters:
|
|
||||||
- name: envoy.router
|
|
||||||
access_log:
|
|
||||||
- name: envoy.file_access_log
|
|
||||||
filter:
|
|
||||||
not_health_check_filter: {}
|
|
||||||
config:
|
|
||||||
json_format:
|
|
||||||
time: "%START_TIME%"
|
|
||||||
protocol: "%PROTOCOL%"
|
|
||||||
duration: "%DURATION%"
|
|
||||||
request_method: "%REQ(:METHOD)%"
|
|
||||||
request_host: "%REQ(HOST)%"
|
|
||||||
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
|
|
||||||
response_flags: "%RESPONSE_FLAGS%"
|
|
||||||
route_name: "%ROUTE_NAME%"
|
|
||||||
upstream_host: "%UPSTREAM_HOST%"
|
|
||||||
upstream_cluster: "%UPSTREAM_CLUSTER%"
|
|
||||||
upstream_local_address: "%UPSTREAM_LOCAL_ADDRESS%"
|
|
||||||
path: "/tmp/access.log"
|
|
||||||
clusters:
|
|
||||||
- name: shoppingagg
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: webshoppingagg
|
|
||||||
port_value: 80
|
|
||||||
- name: catalog
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: catalog-api
|
|
||||||
port_value: 80
|
|
||||||
- name: basket
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: basket-api
|
|
||||||
port_value: 80
|
|
||||||
- name: ordering
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: ordering-api
|
|
||||||
port_value: 80
|
|
||||||
- name: signalr-hub
|
|
||||||
connect_timeout: 0.25s
|
|
||||||
type: strict_dns
|
|
||||||
lb_policy: round_robin
|
|
||||||
hosts:
|
|
||||||
- socket_address:
|
|
||||||
address: ordering-signalrhub
|
|
||||||
port_value: 80
|
|
@ -1,38 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config
|
|
||||||
{
|
|
||||||
public class UrlsConfig
|
|
||||||
{
|
|
||||||
public class CatalogOperations
|
|
||||||
{
|
|
||||||
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
|
|
||||||
|
|
||||||
public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BasketOperations
|
|
||||||
{
|
|
||||||
public static string GetItemById(string id) => $"/api/v1/basket/{id}";
|
|
||||||
|
|
||||||
public static string UpdateBasket() => "/api/v1/basket";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OrdersOperations
|
|
||||||
{
|
|
||||||
public static string GetOrderDraft() => "/api/v1/orders/draft";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Basket { get; set; }
|
|
||||||
|
|
||||||
public string Catalog { get; set; }
|
|
||||||
|
|
||||||
public string Orders { get; set; }
|
|
||||||
|
|
||||||
public string GrpcBasket { get; set; }
|
|
||||||
|
|
||||||
public string GrpcCatalog { get; set; }
|
|
||||||
|
|
||||||
public string GrpcOrdering { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,156 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
|
|
||||||
{
|
|
||||||
[Route("api/v1/[controller]")]
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
public class BasketController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ICatalogService _catalog;
|
|
||||||
private readonly IBasketService _basket;
|
|
||||||
|
|
||||||
public BasketController(ICatalogService catalogService, IBasketService basketService)
|
|
||||||
{
|
|
||||||
_catalog = catalogService;
|
|
||||||
_basket = basketService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[HttpPut]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
|
|
||||||
{
|
|
||||||
if (data.Items == null || !data.Items.Any())
|
|
||||||
{
|
|
||||||
return BadRequest("Need to pass at least one basket line");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the current basket
|
|
||||||
var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId);
|
|
||||||
|
|
||||||
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
|
|
||||||
// group by product id to avoid duplicates
|
|
||||||
var itemsCalculated = data
|
|
||||||
.Items
|
|
||||||
.GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i })
|
|
||||||
.Select(groupedItem =>
|
|
||||||
{
|
|
||||||
var item = groupedItem.items.First();
|
|
||||||
item.Quantity = groupedItem.items.Sum(i => i.Quantity);
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var bitem in itemsCalculated)
|
|
||||||
{
|
|
||||||
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
|
|
||||||
if (catalogItem == null)
|
|
||||||
{
|
|
||||||
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId);
|
|
||||||
if (itemInBasket == null)
|
|
||||||
{
|
|
||||||
basket.Items.Add(new BasketDataItem()
|
|
||||||
{
|
|
||||||
Id = bitem.Id,
|
|
||||||
ProductId = catalogItem.Id,
|
|
||||||
ProductName = catalogItem.Name,
|
|
||||||
PictureUrl = catalogItem.PictureUri,
|
|
||||||
UnitPrice = catalogItem.Price,
|
|
||||||
Quantity = bitem.Quantity
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemInBasket.Quantity = bitem.Quantity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _basket.UpdateAsync(basket);
|
|
||||||
|
|
||||||
return basket;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut]
|
|
||||||
[Route("items")]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
|
|
||||||
{
|
|
||||||
if (!data.Updates.Any())
|
|
||||||
{
|
|
||||||
return BadRequest("No updates sent");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the current basket
|
|
||||||
var currentBasket = await _basket.GetById(data.BasketId);
|
|
||||||
if (currentBasket == null)
|
|
||||||
{
|
|
||||||
return BadRequest($"Basket with id {data.BasketId} not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update with new quantities
|
|
||||||
foreach (var update in data.Updates)
|
|
||||||
{
|
|
||||||
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
|
|
||||||
|
|
||||||
if (basketItem == null)
|
|
||||||
{
|
|
||||||
return BadRequest($"Basket item with id {update.BasketItemId} not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
basketItem.Quantity = update.NewQty;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the updated basket
|
|
||||||
await _basket.UpdateAsync(currentBasket);
|
|
||||||
|
|
||||||
return currentBasket;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Route("items")]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
|
|
||||||
{
|
|
||||||
if (data == null || data.Quantity == 0)
|
|
||||||
{
|
|
||||||
return BadRequest("Invalid payload");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Get the item from catalog
|
|
||||||
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
|
|
||||||
|
|
||||||
//item.PictureUri =
|
|
||||||
|
|
||||||
// Step 2: Get current basket status
|
|
||||||
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
|
|
||||||
// Step 3: Merge current status with new product
|
|
||||||
currentBasket.Items.Add(new BasketDataItem()
|
|
||||||
{
|
|
||||||
UnitPrice = item.Price,
|
|
||||||
PictureUrl = item.PictureUri,
|
|
||||||
ProductId = item.Id,
|
|
||||||
ProductName = item.Name,
|
|
||||||
Quantity = data.Quantity,
|
|
||||||
Id = Guid.NewGuid().ToString()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Step 4: Update basket
|
|
||||||
await _basket.UpdateAsync(currentBasket);
|
|
||||||
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
|
|
||||||
{
|
|
||||||
[Route("")]
|
|
||||||
public class HomeController : Controller
|
|
||||||
{
|
|
||||||
[HttpGet()]
|
|
||||||
public IActionResult Index()
|
|
||||||
{
|
|
||||||
return new RedirectResult("~/swagger");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
|
|
||||||
{
|
|
||||||
[Route("api/v1/[controller]")]
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
public class OrderController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IBasketService _basketService;
|
|
||||||
private readonly IOrderingService _orderingService;
|
|
||||||
|
|
||||||
public OrderController(IBasketService basketService, IOrderingService orderingService)
|
|
||||||
{
|
|
||||||
_basketService = basketService;
|
|
||||||
_orderingService = orderingService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("draft/{basketId}")]
|
|
||||||
[HttpGet]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
[ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(basketId))
|
|
||||||
{
|
|
||||||
return BadRequest("Need a valid basketid");
|
|
||||||
}
|
|
||||||
// Get the basket data and build a order draft based on it
|
|
||||||
var basket = await _basketService.GetById(basketId);
|
|
||||||
|
|
||||||
if (basket == null)
|
|
||||||
{
|
|
||||||
return BadRequest($"No basket found for id {basketId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return await _orderingService.GetOrderDraftAsync(basket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
|
|
||||||
WORKDIR /app
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
|
||||||
WORKDIR /src
|
|
||||||
|
|
||||||
# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles
|
|
||||||
# to take advantage of Docker's build cache, to speed up local container builds
|
|
||||||
COPY "eShopOnContainers-ServicesAndWebApps.sln" "eShopOnContainers-ServicesAndWebApps.sln"
|
|
||||||
|
|
||||||
COPY "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj"
|
|
||||||
COPY "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj"
|
|
||||||
COPY "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj" "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBus/EventBus.csproj" "BuildingBlocks/EventBus/EventBus/EventBus.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj" "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj" "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj"
|
|
||||||
COPY "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj" "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj"
|
|
||||||
COPY "Services/Basket/Basket.API/Basket.API.csproj" "Services/Basket/Basket.API/Basket.API.csproj"
|
|
||||||
COPY "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj" "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj" "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.API/Catalog.API.csproj" "Services/Catalog/Catalog.API/Catalog.API.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj" "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj" "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj"
|
|
||||||
COPY "Services/Identity/Identity.API/Identity.API.csproj" "Services/Identity/Identity.API/Identity.API.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.API/Ordering.API.csproj" "Services/Ordering/Ordering.API/Ordering.API.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj" "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj" "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj" "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj" "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj"
|
|
||||||
COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment.API/Payment.API.csproj"
|
|
||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
|
||||||
|
|
||||||
COPY "docker-compose.dcproj" "docker-compose.dcproj"
|
|
||||||
|
|
||||||
COPY "NuGet.config" "NuGet.config"
|
|
||||||
|
|
||||||
RUN dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
WORKDIR /src/ApiGateways/Mobile.Bff.Shopping/aggregator
|
|
||||||
RUN dotnet publish --no-restore -c Release -o /app
|
|
||||||
|
|
||||||
FROM build AS publish
|
|
||||||
|
|
||||||
FROM base AS final
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=publish /app .
|
|
||||||
ENTRYPOINT ["dotnet", "Mobile.Shopping.HttpAggregator.dll"]
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0
|
|
||||||
ARG BUILD_CONFIGURATION=Debug
|
|
||||||
ENV ASPNETCORE_ENVIRONMENT=Development
|
|
||||||
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
WORKDIR /src
|
|
||||||
COPY ["src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj", "src/ApiGateways/Mobile.Bff.Shopping/aggregator/"]
|
|
||||||
COPY ["src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj", "src/BuildingBlocks/Devspaces.Support/"]
|
|
||||||
COPY ["src/NuGet.config", "src/NuGet.config"]
|
|
||||||
|
|
||||||
RUN dotnet restore src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj -nowarn:msb3202,nu1503
|
|
||||||
COPY . .
|
|
||||||
WORKDIR "/src/src/ApiGateways/Mobile.Bff.Shopping/aggregator"
|
|
||||||
RUN dotnet build -c $BUILD_CONFIGURATION
|
|
||||||
|
|
||||||
CMD ["dotnet", "run", "--no-build", "--no-launch-profile", "-c", "$BUILD_CONFIGURATION", "--"]]
|
|
@ -1,40 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace Basket.API.Infrastructure.Filters
|
|
||||||
{
|
|
||||||
public class AuthorizeCheckOperationFilter : IOperationFilter
|
|
||||||
{
|
|
||||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
|
||||||
{
|
|
||||||
// Check for authorize attribute
|
|
||||||
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() ||
|
|
||||||
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
|
|
||||||
|
|
||||||
if (!hasAuthorize) return;
|
|
||||||
|
|
||||||
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
|
|
||||||
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });
|
|
||||||
|
|
||||||
var oAuthScheme = new OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
|
||||||
};
|
|
||||||
|
|
||||||
operation.Security = new List<OpenApiSecurityRequirement>
|
|
||||||
{
|
|
||||||
new OpenApiSecurityRequirement
|
|
||||||
{
|
|
||||||
[ oAuthScheme ] = new [] { "Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator" }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
using Grpc.Core;
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure
|
|
||||||
{
|
|
||||||
public class GrpcExceptionInterceptor : Interceptor
|
|
||||||
{
|
|
||||||
private readonly ILogger<GrpcExceptionInterceptor> _logger;
|
|
||||||
|
|
||||||
public GrpcExceptionInterceptor(ILogger<GrpcExceptionInterceptor> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
|
|
||||||
TRequest request,
|
|
||||||
ClientInterceptorContext<TRequest, TResponse> context,
|
|
||||||
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
|
||||||
{
|
|
||||||
var call = continuation(request, context);
|
|
||||||
|
|
||||||
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var response = await t;
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
catch (RpcException e)
|
|
||||||
{
|
|
||||||
_logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message);
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure
|
|
||||||
{
|
|
||||||
public class HttpClientAuthorizationDelegatingHandler : DelegatingHandler
|
|
||||||
{
|
|
||||||
private readonly IHttpContextAccessor _httpContextAccesor;
|
|
||||||
private readonly ILogger<HttpClientAuthorizationDelegatingHandler> _logger;
|
|
||||||
|
|
||||||
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor, ILogger<HttpClientAuthorizationDelegatingHandler> logger)
|
|
||||||
{
|
|
||||||
_httpContextAccesor = httpContextAccesor;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
request.Version = new System.Version(2, 0);
|
|
||||||
request.Method = HttpMethod.Get;
|
|
||||||
|
|
||||||
var authorizationHeader = _httpContextAccesor.HttpContext
|
|
||||||
.Request.Headers["Authorization"];
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(authorizationHeader))
|
|
||||||
{
|
|
||||||
request.Headers.Add("Authorization", new List<string>() { authorizationHeader });
|
|
||||||
}
|
|
||||||
|
|
||||||
var token = await GetToken();
|
|
||||||
|
|
||||||
if (token != null)
|
|
||||||
{
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await base.SendAsync(request, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<string> GetToken()
|
|
||||||
{
|
|
||||||
const string ACCESS_TOKEN = "access_token";
|
|
||||||
|
|
||||||
return await _httpContextAccesor.HttpContext
|
|
||||||
.GetTokenAsync(ACCESS_TOKEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
|
||||||
<AssemblyName>Mobile.Shopping.HttpAggregator</AssemblyName>
|
|
||||||
<RootNamespace>Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator</RootNamespace>
|
|
||||||
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
|
||||||
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
|
|
||||||
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
|
|
||||||
<LangVersion>preview</LangVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="wwwroot\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="5.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="5.0.1" />
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.14.0" />
|
|
||||||
<PackageReference Include="Grpc.AspNetCore.Server.ClientFactory" Version="2.34.0" />
|
|
||||||
<PackageReference Include="Grpc.Core" Version="2.34.0" />
|
|
||||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.34.0" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.34.0" PrivateAssets="All" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.2" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.2" />
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\Devspaces.Support\Devspaces.Support.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Protobuf Include="..\..\..\Services\Basket\Basket.API\Proto\basket.proto" GrpcServices="Client" />
|
|
||||||
<Protobuf Include="..\..\..\Services\Catalog\Catalog.API\Proto\catalog.proto" GrpcServices="Client" />
|
|
||||||
<Protobuf Include="..\..\..\Services\Ordering\Ordering.API\Proto\ordering.proto" GrpcServices="Client" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
public class AddBasketItemRequest
|
|
||||||
{
|
|
||||||
public int CatalogItemId { get; set; }
|
|
||||||
|
|
||||||
public string BasketId { get; set; }
|
|
||||||
|
|
||||||
public int Quantity { get; set; }
|
|
||||||
|
|
||||||
public AddBasketItemRequest()
|
|
||||||
{
|
|
||||||
Quantity = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class BasketData
|
|
||||||
{
|
|
||||||
public string BuyerId { get; set; }
|
|
||||||
|
|
||||||
public List<BasketDataItem> Items { get; set; } = new List<BasketDataItem>();
|
|
||||||
|
|
||||||
public BasketData()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasketData(string buyerId)
|
|
||||||
{
|
|
||||||
BuyerId = buyerId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class BasketDataItem
|
|
||||||
{
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
public int ProductId { get; set; }
|
|
||||||
|
|
||||||
public string ProductName { get; set; }
|
|
||||||
|
|
||||||
public decimal UnitPrice { get; set; }
|
|
||||||
|
|
||||||
public decimal OldUnitPrice { get; set; }
|
|
||||||
|
|
||||||
public int Quantity { get; set; }
|
|
||||||
|
|
||||||
public string PictureUrl { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
public class CatalogItem
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
public decimal Price { get; set; }
|
|
||||||
|
|
||||||
public string PictureUri { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class OrderData
|
|
||||||
{
|
|
||||||
public string OrderNumber { get; set; }
|
|
||||||
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
|
|
||||||
public string Status { get; set; }
|
|
||||||
|
|
||||||
public decimal Total { get; set; }
|
|
||||||
|
|
||||||
public string Description { get; set; }
|
|
||||||
|
|
||||||
public string City { get; set; }
|
|
||||||
|
|
||||||
public string Street { get; set; }
|
|
||||||
|
|
||||||
public string State { get; set; }
|
|
||||||
|
|
||||||
public string Country { get; set; }
|
|
||||||
|
|
||||||
public string ZipCode { get; set; }
|
|
||||||
|
|
||||||
public string CardNumber { get; set; }
|
|
||||||
|
|
||||||
public string CardHolderName { get; set; }
|
|
||||||
|
|
||||||
public bool IsDraft { get; set; }
|
|
||||||
|
|
||||||
public DateTime CardExpiration { get; set; }
|
|
||||||
|
|
||||||
public string CardExpirationShort { get; set; }
|
|
||||||
|
|
||||||
public string CardSecurityNumber { get; set; }
|
|
||||||
|
|
||||||
public int CardTypeId { get; set; }
|
|
||||||
|
|
||||||
public string Buyer { get; set; }
|
|
||||||
|
|
||||||
public List<OrderItemData> OrderItems { get; } = new List<OrderItemData>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class OrderItemData
|
|
||||||
{
|
|
||||||
public int ProductId { get; set; }
|
|
||||||
|
|
||||||
public string ProductName { get; set; }
|
|
||||||
|
|
||||||
public decimal UnitPrice { get; set; }
|
|
||||||
|
|
||||||
public decimal Discount { get; set; }
|
|
||||||
|
|
||||||
public int Units { get; set; }
|
|
||||||
|
|
||||||
public string PictureUrl { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class UpdateBasketItemData
|
|
||||||
{
|
|
||||||
public string BasketItemId { get; set; }
|
|
||||||
|
|
||||||
public int NewQty { get; set; }
|
|
||||||
|
|
||||||
public UpdateBasketItemData()
|
|
||||||
{
|
|
||||||
NewQty = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class UpdateBasketItemsRequest
|
|
||||||
{
|
|
||||||
|
|
||||||
public string BasketId { get; set; }
|
|
||||||
|
|
||||||
public ICollection<UpdateBasketItemData> Updates { get; set; }
|
|
||||||
|
|
||||||
public UpdateBasketItemsRequest()
|
|
||||||
{
|
|
||||||
Updates = new List<UpdateBasketItemData>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class UpdateBasketRequest
|
|
||||||
{
|
|
||||||
public string BuyerId { get; set; }
|
|
||||||
|
|
||||||
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class UpdateBasketRequestItemData
|
|
||||||
{
|
|
||||||
public string Id { get; set; } // Basket id
|
|
||||||
|
|
||||||
public int ProductId { get; set; } // Catalog item id
|
|
||||||
|
|
||||||
public int Quantity { get; set; } // Quantity
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
using Microsoft.AspNetCore;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
|
|
||||||
BuildWebHost(args).Run();
|
|
||||||
IWebHost BuildWebHost(string[] args) =>
|
|
||||||
WebHost
|
|
||||||
.CreateDefaultBuilder(args)
|
|
||||||
.ConfigureAppConfiguration(cb =>
|
|
||||||
{
|
|
||||||
var sources = cb.Sources;
|
|
||||||
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource()
|
|
||||||
{
|
|
||||||
Optional = true,
|
|
||||||
Path = "appsettings.localhost.json",
|
|
||||||
ReloadOnChange = false
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.UseStartup<Startup>()
|
|
||||||
.UseSerilog((builderContext, config) =>
|
|
||||||
{
|
|
||||||
config
|
|
||||||
.MinimumLevel.Information()
|
|
||||||
.Enrich.FromLogContext()
|
|
||||||
.WriteTo.Console();
|
|
||||||
})
|
|
||||||
.Build();
|
|
@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:57425/",
|
|
||||||
"sslPort": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "api/values",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseForMvc": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "api/values",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:61632/"
|
|
||||||
},
|
|
||||||
"Azure Dev Spaces": {
|
|
||||||
"commandName": "AzureDevSpaces",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"resourceGroup": "eshoptestedu",
|
|
||||||
"aksName": "eshoptestedu",
|
|
||||||
"subscriptionId": "e3035ac1-c06c-4daf-8939-57b3c5f1f759"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
using GrpcBasket;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public class BasketService : IBasketService
|
|
||||||
{
|
|
||||||
private readonly Basket.BasketClient _basketClient;
|
|
||||||
private readonly ILogger<BasketService> _logger;
|
|
||||||
|
|
||||||
public BasketService(Basket.BasketClient basketClient, ILogger<BasketService> logger)
|
|
||||||
{
|
|
||||||
_basketClient = basketClient;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<BasketData> GetById(string id)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("grpc client created, request = {@id}", id);
|
|
||||||
var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id });
|
|
||||||
_logger.LogDebug("grpc response {@response}", response);
|
|
||||||
|
|
||||||
return MapToBasketData(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpdateAsync(BasketData currentBasket)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
|
|
||||||
var request = MapToCustomerBasketRequest(currentBasket);
|
|
||||||
_logger.LogDebug("Grpc update basket request {@request}", request);
|
|
||||||
|
|
||||||
await _basketClient.UpdateBasketAsync(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest)
|
|
||||||
{
|
|
||||||
if (customerBasketRequest == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var map = new BasketData
|
|
||||||
{
|
|
||||||
BuyerId = customerBasketRequest.Buyerid
|
|
||||||
};
|
|
||||||
|
|
||||||
customerBasketRequest.Items.ToList().ForEach(item => map.Items.Add(new BasketDataItem
|
|
||||||
{
|
|
||||||
Id = item.Id,
|
|
||||||
OldUnitPrice = (decimal)item.Oldunitprice,
|
|
||||||
PictureUrl = item.Pictureurl,
|
|
||||||
ProductId = item.Productid,
|
|
||||||
ProductName = item.Productname,
|
|
||||||
Quantity = item.Quantity,
|
|
||||||
UnitPrice = (decimal)item.Unitprice
|
|
||||||
}));
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData)
|
|
||||||
{
|
|
||||||
if (basketData == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var map = new CustomerBasketRequest
|
|
||||||
{
|
|
||||||
Buyerid = basketData.BuyerId
|
|
||||||
};
|
|
||||||
|
|
||||||
basketData.Items.ToList().ForEach(item => map.Items.Add(new BasketItemResponse
|
|
||||||
{
|
|
||||||
Id = item.Id,
|
|
||||||
Oldunitprice = (double)item.OldUnitPrice,
|
|
||||||
Pictureurl = item.PictureUrl,
|
|
||||||
Productid = item.ProductId,
|
|
||||||
Productname = item.ProductName,
|
|
||||||
Quantity = item.Quantity,
|
|
||||||
Unitprice = (double)item.UnitPrice
|
|
||||||
}));
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
using CatalogApi;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public class CatalogService : ICatalogService
|
|
||||||
{
|
|
||||||
private readonly Catalog.CatalogClient _client;
|
|
||||||
|
|
||||||
public CatalogService(Catalog.CatalogClient client)
|
|
||||||
{
|
|
||||||
_client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<CatalogItem> GetCatalogItemAsync(int id)
|
|
||||||
{
|
|
||||||
var request = new CatalogItemRequest { Id = id };
|
|
||||||
var response = await _client.GetItemByIdAsync(request);
|
|
||||||
return MapToCatalogItemResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
|
|
||||||
{
|
|
||||||
var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 };
|
|
||||||
var response = await _client.GetItemsByIdsAsync(request);
|
|
||||||
return response.Data.Select(MapToCatalogItemResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse)
|
|
||||||
{
|
|
||||||
return new CatalogItem
|
|
||||||
{
|
|
||||||
Id = catalogItemResponse.Id,
|
|
||||||
Name = catalogItemResponse.Name,
|
|
||||||
PictureUri = catalogItemResponse.PictureUri,
|
|
||||||
Price = (decimal)catalogItemResponse.Price
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public interface IBasketService
|
|
||||||
{
|
|
||||||
Task<BasketData> GetById(string id);
|
|
||||||
|
|
||||||
Task UpdateAsync(BasketData currentBasket);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public interface ICatalogService
|
|
||||||
{
|
|
||||||
Task<CatalogItem> GetCatalogItemAsync(int id);
|
|
||||||
|
|
||||||
Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public interface IOrderApiClient
|
|
||||||
{
|
|
||||||
Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public interface IOrderingService
|
|
||||||
{
|
|
||||||
Task<OrderData> GetOrderDraftAsync(BasketData basketData);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public class OrderApiClient : IOrderApiClient
|
|
||||||
{
|
|
||||||
private readonly HttpClient _apiClient;
|
|
||||||
private readonly ILogger<OrderApiClient> _logger;
|
|
||||||
private readonly UrlsConfig _urls;
|
|
||||||
|
|
||||||
public OrderApiClient(HttpClient httpClient, ILogger<OrderApiClient> logger, IOptions<UrlsConfig> config)
|
|
||||||
{
|
|
||||||
_apiClient = httpClient;
|
|
||||||
_logger = logger;
|
|
||||||
_urls = config.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket)
|
|
||||||
{
|
|
||||||
var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
|
|
||||||
var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json");
|
|
||||||
var response = await _apiClient.PostAsync(uri, content);
|
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
|
|
||||||
var ordersDraftResponse = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
return JsonSerializer.Deserialize<OrderData>(ordersDraftResponse, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
using GrpcOrdering;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public class OrderingService : IOrderingService
|
|
||||||
{
|
|
||||||
private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
|
|
||||||
private readonly ILogger<OrderingService> _logger;
|
|
||||||
|
|
||||||
public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
|
|
||||||
{
|
|
||||||
_orderingGrpcClient = orderingGrpcClient;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OrderData> GetOrderDraftAsync(BasketData basketData)
|
|
||||||
{
|
|
||||||
_logger.LogDebug(" grpc client created, basketData={@basketData}", basketData);
|
|
||||||
|
|
||||||
var command = MapToOrderDraftCommand(basketData);
|
|
||||||
var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command);
|
|
||||||
_logger.LogDebug(" grpc response: {@response}", response);
|
|
||||||
|
|
||||||
return MapToResponse(response, basketData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData)
|
|
||||||
{
|
|
||||||
if (orderDraft == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = new OrderData
|
|
||||||
{
|
|
||||||
Buyer = basketData.BuyerId,
|
|
||||||
Total = (decimal)orderDraft.Total,
|
|
||||||
};
|
|
||||||
|
|
||||||
orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData
|
|
||||||
{
|
|
||||||
Discount = (decimal)o.Discount,
|
|
||||||
PictureUrl = o.PictureUrl,
|
|
||||||
ProductId = o.ProductId,
|
|
||||||
ProductName = o.ProductName,
|
|
||||||
UnitPrice = (decimal)o.UnitPrice,
|
|
||||||
Units = o.Units,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
|
|
||||||
{
|
|
||||||
var command = new CreateOrderDraftCommand
|
|
||||||
{
|
|
||||||
BuyerId = basketData.BuyerId,
|
|
||||||
};
|
|
||||||
|
|
||||||
basketData.Items.ForEach(i => command.Items.Add(new BasketItem
|
|
||||||
{
|
|
||||||
Id = i.Id,
|
|
||||||
OldUnitPrice = (double)i.OldUnitPrice,
|
|
||||||
PictureUrl = i.PictureUrl,
|
|
||||||
ProductId = i.ProductId,
|
|
||||||
ProductName = i.ProductName,
|
|
||||||
Quantity = i.Quantity,
|
|
||||||
UnitPrice = (double)i.UnitPrice,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,222 +0,0 @@
|
|||||||
using CatalogApi;
|
|
||||||
using Devspaces.Support;
|
|
||||||
using GrpcBasket;
|
|
||||||
using GrpcOrdering;
|
|
||||||
using HealthChecks.UI.Client;
|
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure;
|
|
||||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
|
|
||||||
{
|
|
||||||
public class Startup
|
|
||||||
{
|
|
||||||
public Startup(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
Configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IConfiguration Configuration { get; }
|
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddHealthChecks()
|
|
||||||
.AddCheck("self", () => HealthCheckResult.Healthy())
|
|
||||||
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
|
|
||||||
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
|
|
||||||
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" })
|
|
||||||
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
|
|
||||||
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" });
|
|
||||||
|
|
||||||
services.AddCustomMvc(Configuration)
|
|
||||||
.AddCustomAuthentication(Configuration)
|
|
||||||
.AddDevspaces()
|
|
||||||
.AddHttpServices()
|
|
||||||
.AddGrpcServices();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
||||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
|
|
||||||
{
|
|
||||||
var pathBase = Configuration["PATH_BASE"];
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(pathBase))
|
|
||||||
{
|
|
||||||
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
|
|
||||||
app.UsePathBase(pathBase);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseDeveloperExceptionPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseSwagger().UseSwaggerUI(c =>
|
|
||||||
{
|
|
||||||
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
|
|
||||||
|
|
||||||
c.OAuthClientId("mobileshoppingaggswaggerui");
|
|
||||||
c.OAuthClientSecret(string.Empty);
|
|
||||||
c.OAuthRealm(string.Empty);
|
|
||||||
c.OAuthAppName("Purchase BFF Swagger UI");
|
|
||||||
});
|
|
||||||
|
|
||||||
app.UseRouting();
|
|
||||||
app.UseCors("CorsPolicy");
|
|
||||||
app.UseAuthentication();
|
|
||||||
app.UseAuthorization();
|
|
||||||
app.UseEndpoints(endpoints =>
|
|
||||||
{
|
|
||||||
endpoints.MapDefaultControllerRoute();
|
|
||||||
endpoints.MapControllers();
|
|
||||||
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
|
|
||||||
{
|
|
||||||
Predicate = _ => true,
|
|
||||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
|
||||||
});
|
|
||||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
|
||||||
{
|
|
||||||
Predicate = r => r.Name.Contains("self")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.AddOptions();
|
|
||||||
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
|
|
||||||
|
|
||||||
services.AddControllers()
|
|
||||||
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
|
|
||||||
|
|
||||||
services.AddSwaggerGen(options =>
|
|
||||||
{
|
|
||||||
options.DescribeAllEnumsAsStrings();
|
|
||||||
options.SwaggerDoc("v1", new OpenApiInfo
|
|
||||||
{
|
|
||||||
Title = "Shopping Aggregator for Mobile Clients",
|
|
||||||
Version = "v1",
|
|
||||||
Description = "Shopping Aggregator for Mobile Clients"
|
|
||||||
});
|
|
||||||
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Type = SecuritySchemeType.OAuth2,
|
|
||||||
Flows = new OpenApiOAuthFlows()
|
|
||||||
{
|
|
||||||
Implicit = new OpenApiOAuthFlow()
|
|
||||||
{
|
|
||||||
AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"),
|
|
||||||
TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"),
|
|
||||||
|
|
||||||
Scopes = new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
options.OperationFilter<AuthorizeCheckOperationFilter>();
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddCors(options =>
|
|
||||||
{
|
|
||||||
options.AddPolicy("CorsPolicy",
|
|
||||||
builder => builder
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.SetIsOriginAllowed((host) => true)
|
|
||||||
.AllowCredentials());
|
|
||||||
});
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
|
|
||||||
|
|
||||||
var identityUrl = configuration.GetValue<string>("urls:identity");
|
|
||||||
|
|
||||||
services.AddAuthentication(options =>
|
|
||||||
{
|
|
||||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
||||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
||||||
|
|
||||||
})
|
|
||||||
.AddJwtBearer(options =>
|
|
||||||
{
|
|
||||||
options.Authority = identityUrl;
|
|
||||||
options.RequireHttpsMetadata = false;
|
|
||||||
options.Audience = "mobileshoppingagg";
|
|
||||||
});
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddHttpServices(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
//register delegating handlers
|
|
||||||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
|
|
||||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
|
||||||
|
|
||||||
//register http services
|
|
||||||
|
|
||||||
services.AddHttpClient<IOrderApiClient, OrderApiClient>()
|
|
||||||
.AddDevspacesSupport();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddGrpcServices(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddTransient<GrpcExceptionInterceptor>();
|
|
||||||
|
|
||||||
services.AddScoped<IBasketService, BasketService>();
|
|
||||||
|
|
||||||
services.AddGrpcClient<Basket.BasketClient>((services, options) =>
|
|
||||||
{
|
|
||||||
var basketApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcBasket;
|
|
||||||
options.Address = new Uri(basketApi);
|
|
||||||
}).AddInterceptor<GrpcExceptionInterceptor>();
|
|
||||||
|
|
||||||
services.AddScoped<ICatalogService, CatalogService>();
|
|
||||||
|
|
||||||
services.AddGrpcClient<Catalog.CatalogClient>((services, options) =>
|
|
||||||
{
|
|
||||||
var catalogApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcCatalog;
|
|
||||||
options.Address = new Uri(catalogApi);
|
|
||||||
}).AddInterceptor<GrpcExceptionInterceptor>();
|
|
||||||
|
|
||||||
services.AddScoped<IOrderingService, OrderingService>();
|
|
||||||
|
|
||||||
services.AddGrpcClient<OrderingGrpc.OrderingGrpcClient>((services, options) =>
|
|
||||||
{
|
|
||||||
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering;
|
|
||||||
options.Address = new Uri(orderingApi);
|
|
||||||
}).AddInterceptor<GrpcExceptionInterceptor>();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"IncludeScopes": false,
|
|
||||||
"Debug": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Console": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"urls": {
|
|
||||||
"basket": "http://localhost:55105",
|
|
||||||
"catalog": "http://localhost:55101",
|
|
||||||
"orders": "http://localhost:55102",
|
|
||||||
"identity": "http://localhost:55105",
|
|
||||||
"grpcBasket": "http://localhost:5580",
|
|
||||||
"grpcCatalog": "http://localhost:81",
|
|
||||||
"grpcOrdering": "http://localhost:5581"
|
|
||||||
},
|
|
||||||
"IdentityUrlExternal": "http://localhost:5105",
|
|
||||||
"IdentityUrl": "http://localhost:5105",
|
|
||||||
"Logging": {
|
|
||||||
"IncludeScopes": false,
|
|
||||||
"Debug": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Debug"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Console": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Debug"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
kind: helm-release
|
|
||||||
apiVersion: 1.1
|
|
||||||
build:
|
|
||||||
context: ..\..\..\..
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
install:
|
|
||||||
chart: ../../../../k8s/helm/mobileshoppingagg
|
|
||||||
set:
|
|
||||||
image:
|
|
||||||
tag: $(tag)
|
|
||||||
pullPolicy: Never
|
|
||||||
ingress:
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: traefik-azds
|
|
||||||
hosts:
|
|
||||||
# This expands to [space.s.]apigwms.<guid>.<region>.aksapp.io
|
|
||||||
- $(spacePrefix)eshop$(hostSuffix)
|
|
||||||
inf:
|
|
||||||
k8s:
|
|
||||||
dns: $(spacePrefix)eshop$(hostSuffix)
|
|
||||||
values:
|
|
||||||
- values.dev.yaml?
|
|
||||||
- secrets.dev.yaml?
|
|
||||||
- app.yaml
|
|
||||||
- inf.yaml
|
|
||||||
configurations:
|
|
||||||
develop:
|
|
||||||
build:
|
|
||||||
useGitIgnore: true
|
|
||||||
dockerfile: Dockerfile.develop
|
|
||||||
container:
|
|
||||||
syncTarget: /src
|
|
||||||
sync:
|
|
||||||
- '**/Pages/**'
|
|
||||||
- '**/Views/**'
|
|
||||||
- '**/wwwroot/**'
|
|
||||||
- '!**/*.{sln,csproj}'
|
|
||||||
command:
|
|
||||||
- dotnet
|
|
||||||
- run
|
|
||||||
- --no-restore
|
|
||||||
- --no-build
|
|
||||||
- --no-launch-profile
|
|
||||||
- -c
|
|
||||||
- ${Configuration:-Debug}
|
|
||||||
iterate:
|
|
||||||
processesToKill:
|
|
||||||
- dotnet
|
|
||||||
- vsdbg
|
|
||||||
buildCommands:
|
|
||||||
- - dotnet
|
|
||||||
- build
|
|
||||||
- --no-restore
|
|
||||||
- -c
|
|
||||||
- ${Configuration:-Debug}
|
|
@ -1,3 +0,0 @@
|
|||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
tls: []
|
|
@ -1,44 +0,0 @@
|
|||||||
kind: helm-release
|
|
||||||
apiVersion: 1.1
|
|
||||||
build:
|
|
||||||
context: ..\..\..\..\
|
|
||||||
dockerfile: ..\..\..\ApiGateways\ApiGw-Base\Dockerfile
|
|
||||||
install:
|
|
||||||
chart: ../../../../k8s/helm/apigwms
|
|
||||||
set:
|
|
||||||
replicaCount: 1
|
|
||||||
image:
|
|
||||||
tag: $(tag)
|
|
||||||
pullPolicy: Never
|
|
||||||
ingress:
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: traefik-azds
|
|
||||||
hosts:
|
|
||||||
# This expands to [space.s.]webmvc.<guid>.<region>.aksapp.io
|
|
||||||
- $(spacePrefix)eshop$(hostSuffix)
|
|
||||||
inf:
|
|
||||||
k8s:
|
|
||||||
dns: $(spacePrefix)eshop$(hostSuffix)
|
|
||||||
values:
|
|
||||||
- values.dev.yaml?
|
|
||||||
- secrets.dev.yaml?
|
|
||||||
- inf.yaml
|
|
||||||
- app.yaml
|
|
||||||
configurations:
|
|
||||||
develop:
|
|
||||||
build:
|
|
||||||
useGitIgnore: true
|
|
||||||
dockerfile: ..\..\..\ApiGateways\ApiGw-Base\Dockerfile.develop
|
|
||||||
args:
|
|
||||||
BUILD_CONFIGURATION: ${BUILD_CONFIGURATION:-Debug}
|
|
||||||
container:
|
|
||||||
sync:
|
|
||||||
- '**/Pages/**'
|
|
||||||
- '**/Views/**'
|
|
||||||
- '**/wwwroot/**'
|
|
||||||
- '!**/*.{sln,csproj}'
|
|
||||||
command: [dotnet, run, --no-restore, --no-build, --no-launch-profile, -c, "${BUILD_CONFIGURATION:-Debug}"]
|
|
||||||
iterate:
|
|
||||||
processesToKill: [dotnet, vsdbg]
|
|
||||||
buildCommands:
|
|
||||||
- [dotnet, build, --no-restore, -c, "${BUILD_CONFIGURATION:-Debug}"]
|
|
@ -1,118 +0,0 @@
|
|||||||
{
|
|
||||||
"ReRoutes": [
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/{version}/{everything}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "catalog.api",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/api/{version}/c/{everything}",
|
|
||||||
"UpstreamHttpMethod": [ "GET" ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/{version}/{everything}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "basket.api",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/api/{version}/b/{everything}",
|
|
||||||
"UpstreamHttpMethod": [],
|
|
||||||
"AuthenticationOptions": {
|
|
||||||
"AuthenticationProviderKey": "IdentityApiKey",
|
|
||||||
"AllowedScopes": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/{version}/{everything}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "ordering.api",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/api/{version}/o/{everything}",
|
|
||||||
"UpstreamHttpMethod": [],
|
|
||||||
"AuthenticationOptions": {
|
|
||||||
"AuthenticationProviderKey": "IdentityApiKey",
|
|
||||||
"AllowedScopes": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/{everything}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "mobileshoppingagg",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/{everything}",
|
|
||||||
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
|
|
||||||
"AuthenticationOptions": {
|
|
||||||
"AuthenticationProviderKey": "IdentityApiKey",
|
|
||||||
"AllowedScopes": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/{everything}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "ordering.api",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/orders-api/{everything}",
|
|
||||||
"UpstreamHttpMethod": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/{everything}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "basket.api",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/basket-api/{everything}",
|
|
||||||
"UpstreamHttpMethod": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/{everything}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "catalog.api",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/catalog-api/{everything}",
|
|
||||||
"UpstreamHttpMethod": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/{everything}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "payment.api",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/payment-api/{everything}",
|
|
||||||
"UpstreamHttpMethod": []
|
|
||||||
}
|
|
||||||
|
|
||||||
],
|
|
||||||
"GlobalConfiguration": {
|
|
||||||
"RequestIdKey": "OcRequestId",
|
|
||||||
"AdministrationPath": "/administration"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
ocelot:
|
|
||||||
configPath: /src/src/ApiGateways/ApiGw-Base/configuration
|
|
@ -1,45 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config
|
|
||||||
{
|
|
||||||
|
|
||||||
public class UrlsConfig
|
|
||||||
{
|
|
||||||
|
|
||||||
public class CatalogOperations
|
|
||||||
{
|
|
||||||
// grpc call under REST must go trough port 80
|
|
||||||
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
|
|
||||||
|
|
||||||
public static string GetItemById(string ids) => $"/api/v1/catalog/items/ids/{string.Join(',', ids)}";
|
|
||||||
|
|
||||||
// REST call standard must go through port 5000
|
|
||||||
public static string GetItemsById(IEnumerable<int> ids) => $":5000/api/v1/catalog/items?ids={string.Join(',', ids)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BasketOperations
|
|
||||||
{
|
|
||||||
public static string GetItemById(string id) => $"/api/v1/basket/{id}";
|
|
||||||
|
|
||||||
public static string UpdateBasket() => "/api/v1/basket";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OrdersOperations
|
|
||||||
{
|
|
||||||
public static string GetOrderDraft() => "/api/v1/orders/draft";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Basket { get; set; }
|
|
||||||
|
|
||||||
public string Catalog { get; set; }
|
|
||||||
|
|
||||||
public string Orders { get; set; }
|
|
||||||
|
|
||||||
public string GrpcBasket { get; set; }
|
|
||||||
|
|
||||||
public string GrpcCatalog { get; set; }
|
|
||||||
|
|
||||||
public string GrpcOrdering { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
|
|
||||||
{
|
|
||||||
[Route("api/v1/[controller]")]
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
public class BasketController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ICatalogService _catalog;
|
|
||||||
private readonly IBasketService _basket;
|
|
||||||
|
|
||||||
public BasketController(ICatalogService catalogService, IBasketService basketService)
|
|
||||||
{
|
|
||||||
_catalog = catalogService;
|
|
||||||
_basket = basketService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[HttpPut]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
|
|
||||||
{
|
|
||||||
if (data.Items == null || !data.Items.Any())
|
|
||||||
{
|
|
||||||
return BadRequest("Need to pass at least one basket line");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the current basket
|
|
||||||
var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId);
|
|
||||||
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
|
|
||||||
|
|
||||||
// group by product id to avoid duplicates
|
|
||||||
var itemsCalculated = data
|
|
||||||
.Items
|
|
||||||
.GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i })
|
|
||||||
.Select(groupedItem =>
|
|
||||||
{
|
|
||||||
var item = groupedItem.items.First();
|
|
||||||
item.Quantity = groupedItem.items.Sum(i => i.Quantity);
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var bitem in itemsCalculated)
|
|
||||||
{
|
|
||||||
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
|
|
||||||
if (catalogItem == null)
|
|
||||||
{
|
|
||||||
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId);
|
|
||||||
if (itemInBasket == null)
|
|
||||||
{
|
|
||||||
basket.Items.Add(new BasketDataItem()
|
|
||||||
{
|
|
||||||
Id = bitem.Id,
|
|
||||||
ProductId = catalogItem.Id,
|
|
||||||
ProductName = catalogItem.Name,
|
|
||||||
PictureUrl = catalogItem.PictureUri,
|
|
||||||
UnitPrice = catalogItem.Price,
|
|
||||||
Quantity = bitem.Quantity
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemInBasket.Quantity = bitem.Quantity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _basket.UpdateAsync(basket);
|
|
||||||
|
|
||||||
return basket;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut]
|
|
||||||
[Route("items")]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
|
|
||||||
{
|
|
||||||
if (!data.Updates.Any())
|
|
||||||
{
|
|
||||||
return BadRequest("No updates sent");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the current basket
|
|
||||||
var currentBasket = await _basket.GetById(data.BasketId);
|
|
||||||
if (currentBasket == null)
|
|
||||||
{
|
|
||||||
return BadRequest($"Basket with id {data.BasketId} not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update with new quantities
|
|
||||||
foreach (var update in data.Updates)
|
|
||||||
{
|
|
||||||
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
|
|
||||||
if (basketItem == null)
|
|
||||||
{
|
|
||||||
return BadRequest($"Basket item with id {update.BasketItemId} not found");
|
|
||||||
}
|
|
||||||
basketItem.Quantity = update.NewQty;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the updated basket
|
|
||||||
await _basket.UpdateAsync(currentBasket);
|
|
||||||
|
|
||||||
return currentBasket;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Route("items")]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
|
|
||||||
{
|
|
||||||
if (data == null || data.Quantity == 0)
|
|
||||||
{
|
|
||||||
return BadRequest("Invalid payload");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Get the item from catalog
|
|
||||||
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
|
|
||||||
|
|
||||||
//item.PictureUri =
|
|
||||||
|
|
||||||
// Step 2: Get current basket status
|
|
||||||
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
|
|
||||||
// Step 3: Search if exist product into basket
|
|
||||||
var product = currentBasket.Items.SingleOrDefault(i => i.ProductId == item.Id);
|
|
||||||
if (product != null)
|
|
||||||
{
|
|
||||||
// Step 4: Update quantity for product
|
|
||||||
product.Quantity += data.Quantity;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Step 4: Merge current status with new product
|
|
||||||
currentBasket.Items.Add(new BasketDataItem()
|
|
||||||
{
|
|
||||||
UnitPrice = item.Price,
|
|
||||||
PictureUrl = item.PictureUri,
|
|
||||||
ProductId = item.Id,
|
|
||||||
ProductName = item.Name,
|
|
||||||
Quantity = data.Quantity,
|
|
||||||
Id = Guid.NewGuid().ToString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 5: Update basket
|
|
||||||
await _basket.UpdateAsync(currentBasket);
|
|
||||||
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
|
|
||||||
{
|
|
||||||
[Route("")]
|
|
||||||
public class HomeController : Controller
|
|
||||||
{
|
|
||||||
[HttpGet()]
|
|
||||||
public IActionResult Index()
|
|
||||||
{
|
|
||||||
return new RedirectResult("~/swagger");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
|
|
||||||
{
|
|
||||||
[Route("api/v1/[controller]")]
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
public class OrderController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IBasketService _basketService;
|
|
||||||
private readonly IOrderingService _orderingService;
|
|
||||||
public OrderController(IBasketService basketService, IOrderingService orderingService)
|
|
||||||
{
|
|
||||||
_basketService = basketService;
|
|
||||||
_orderingService = orderingService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("draft/{basketId}")]
|
|
||||||
[HttpGet]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
[ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(basketId))
|
|
||||||
{
|
|
||||||
return BadRequest("Need a valid basketid");
|
|
||||||
}
|
|
||||||
// Get the basket data and build a order draft based on it
|
|
||||||
var basket = await _basketService.GetById(basketId);
|
|
||||||
|
|
||||||
if (basket == null)
|
|
||||||
{
|
|
||||||
return BadRequest($"No basket found for id {basketId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return await _orderingService.GetOrderDraftAsync(basket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
|
|
||||||
WORKDIR /app
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
|
||||||
WORKDIR /src
|
|
||||||
|
|
||||||
# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles
|
|
||||||
# to take advantage of Docker's build cache, to speed up local container builds
|
|
||||||
COPY "eShopOnContainers-ServicesAndWebApps.sln" "eShopOnContainers-ServicesAndWebApps.sln"
|
|
||||||
|
|
||||||
COPY "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj"
|
|
||||||
COPY "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj"
|
|
||||||
COPY "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj" "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBus/EventBus.csproj" "BuildingBlocks/EventBus/EventBus/EventBus.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj" "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj" "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj"
|
|
||||||
COPY "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj" "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj"
|
|
||||||
COPY "Services/Basket/Basket.API/Basket.API.csproj" "Services/Basket/Basket.API/Basket.API.csproj"
|
|
||||||
COPY "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj" "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj" "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.API/Catalog.API.csproj" "Services/Catalog/Catalog.API/Catalog.API.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj" "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj" "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj"
|
|
||||||
COPY "Services/Identity/Identity.API/Identity.API.csproj" "Services/Identity/Identity.API/Identity.API.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.API/Ordering.API.csproj" "Services/Ordering/Ordering.API/Ordering.API.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj" "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj" "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj" "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj" "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj"
|
|
||||||
COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment.API/Payment.API.csproj"
|
|
||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
|
||||||
|
|
||||||
COPY "docker-compose.dcproj" "docker-compose.dcproj"
|
|
||||||
|
|
||||||
COPY "NuGet.config" "NuGet.config"
|
|
||||||
|
|
||||||
RUN dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
WORKDIR /src/ApiGateways/Web.Bff.Shopping/aggregator
|
|
||||||
RUN dotnet publish --no-restore -c Release -o /app
|
|
||||||
|
|
||||||
FROM build AS publish
|
|
||||||
|
|
||||||
FROM base AS final
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=publish /app .
|
|
||||||
ENTRYPOINT ["dotnet", "Web.Shopping.HttpAggregator.dll"]
|
|
@ -1,17 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0
|
|
||||||
ARG BUILD_CONFIGURATION=Debug
|
|
||||||
ENV ASPNETCORE_ENVIRONMENT=Development
|
|
||||||
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
WORKDIR /src
|
|
||||||
COPY ["src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj", "src/ApiGateways/Web.Bff.Shopping/aggregator/"]
|
|
||||||
COPY ["src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj", "src/BuildingBlocks/Devspaces.Support/"]
|
|
||||||
COPY ["src/NuGet.config", "src/NuGet.config"]
|
|
||||||
|
|
||||||
RUN dotnet restore src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj -nowarn:msb3202,nu1503
|
|
||||||
COPY . .
|
|
||||||
WORKDIR "/src/src/ApiGateways/Web.Bff.Shopping/aggregator"
|
|
||||||
RUN dotnet build -c $BUILD_CONFIGURATION
|
|
||||||
|
|
||||||
CMD ["dotnet", "run", "--no-build", "--no-launch-profile", "-c", "$BUILD_CONFIGURATION", "--"]]
|
|
@ -1,39 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters
|
|
||||||
{
|
|
||||||
namespace Basket.API.Infrastructure.Filters
|
|
||||||
{
|
|
||||||
public class AuthorizeCheckOperationFilter : IOperationFilter
|
|
||||||
{
|
|
||||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
|
||||||
{
|
|
||||||
// Check for authorize attribute
|
|
||||||
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() ||
|
|
||||||
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
|
|
||||||
|
|
||||||
if (!hasAuthorize) return;
|
|
||||||
|
|
||||||
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
|
|
||||||
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });
|
|
||||||
|
|
||||||
var oAuthScheme = new OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
|
||||||
};
|
|
||||||
|
|
||||||
operation.Security = new List<OpenApiSecurityRequirement>
|
|
||||||
{
|
|
||||||
new OpenApiSecurityRequirement
|
|
||||||
{
|
|
||||||
[ oAuthScheme ] = new [] { "Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator" }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
using Grpc.Core;
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure
|
|
||||||
{
|
|
||||||
public class GrpcExceptionInterceptor : Interceptor
|
|
||||||
{
|
|
||||||
private readonly ILogger<GrpcExceptionInterceptor> _logger;
|
|
||||||
|
|
||||||
public GrpcExceptionInterceptor(ILogger<GrpcExceptionInterceptor> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
|
|
||||||
TRequest request,
|
|
||||||
ClientInterceptorContext<TRequest, TResponse> context,
|
|
||||||
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
|
||||||
{
|
|
||||||
var call = continuation(request, context);
|
|
||||||
|
|
||||||
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var response = await t;
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
catch (RpcException e)
|
|
||||||
{
|
|
||||||
_logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message);
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure
|
|
||||||
{
|
|
||||||
public class HttpClientAuthorizationDelegatingHandler
|
|
||||||
: DelegatingHandler
|
|
||||||
{
|
|
||||||
private readonly IHttpContextAccessor _httpContextAccesor;
|
|
||||||
|
|
||||||
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor)
|
|
||||||
{
|
|
||||||
_httpContextAccesor = httpContextAccesor;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var authorizationHeader = _httpContextAccesor.HttpContext
|
|
||||||
.Request.Headers["Authorization"];
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(authorizationHeader))
|
|
||||||
{
|
|
||||||
request.Headers.Add("Authorization", new List<string>() { authorizationHeader });
|
|
||||||
}
|
|
||||||
|
|
||||||
var token = await GetToken();
|
|
||||||
|
|
||||||
if (token != null)
|
|
||||||
{
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await base.SendAsync(request, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<string> GetToken()
|
|
||||||
{
|
|
||||||
const string ACCESS_TOKEN = "access_token";
|
|
||||||
|
|
||||||
return await _httpContextAccesor.HttpContext
|
|
||||||
.GetTokenAsync(ACCESS_TOKEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class AddBasketItemRequest
|
|
||||||
{
|
|
||||||
public int CatalogItemId { get; set; }
|
|
||||||
|
|
||||||
public string BasketId { get; set; }
|
|
||||||
|
|
||||||
public int Quantity { get; set; }
|
|
||||||
|
|
||||||
public AddBasketItemRequest()
|
|
||||||
{
|
|
||||||
Quantity = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class BasketData
|
|
||||||
{
|
|
||||||
public string BuyerId { get; set; }
|
|
||||||
|
|
||||||
public List<BasketDataItem> Items { get; set; } = new List<BasketDataItem>();
|
|
||||||
|
|
||||||
public BasketData()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasketData(string buyerId)
|
|
||||||
{
|
|
||||||
BuyerId = buyerId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class BasketDataItem
|
|
||||||
{
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
public int ProductId { get; set; }
|
|
||||||
|
|
||||||
public string ProductName { get; set; }
|
|
||||||
|
|
||||||
public decimal UnitPrice { get; set; }
|
|
||||||
|
|
||||||
public decimal OldUnitPrice { get; set; }
|
|
||||||
|
|
||||||
public int Quantity { get; set; }
|
|
||||||
|
|
||||||
public string PictureUrl { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class CatalogItem
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
public decimal Price { get; set; }
|
|
||||||
|
|
||||||
public string PictureUri { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class OrderData
|
|
||||||
{
|
|
||||||
public string OrderNumber { get; set; }
|
|
||||||
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
|
|
||||||
public string Status { get; set; }
|
|
||||||
|
|
||||||
public decimal Total { get; set; }
|
|
||||||
|
|
||||||
public string Description { get; set; }
|
|
||||||
|
|
||||||
public string City { get; set; }
|
|
||||||
|
|
||||||
public string Street { get; set; }
|
|
||||||
|
|
||||||
public string State { get; set; }
|
|
||||||
|
|
||||||
public string Country { get; set; }
|
|
||||||
|
|
||||||
public string ZipCode { get; set; }
|
|
||||||
|
|
||||||
public string CardNumber { get; set; }
|
|
||||||
|
|
||||||
public string CardHolderName { get; set; }
|
|
||||||
|
|
||||||
public bool IsDraft { get; set; }
|
|
||||||
|
|
||||||
public DateTime CardExpiration { get; set; }
|
|
||||||
|
|
||||||
public string CardExpirationShort { get; set; }
|
|
||||||
|
|
||||||
public string CardSecurityNumber { get; set; }
|
|
||||||
|
|
||||||
public int CardTypeId { get; set; }
|
|
||||||
|
|
||||||
public string Buyer { get; set; }
|
|
||||||
|
|
||||||
public List<OrderItemData> OrderItems { get; } = new List<OrderItemData>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class OrderItemData
|
|
||||||
{
|
|
||||||
public int ProductId { get; set; }
|
|
||||||
|
|
||||||
public string ProductName { get; set; }
|
|
||||||
|
|
||||||
public decimal UnitPrice { get; set; }
|
|
||||||
|
|
||||||
public decimal Discount { get; set; }
|
|
||||||
|
|
||||||
public int Units { get; set; }
|
|
||||||
|
|
||||||
public string PictureUrl { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class UpdateBasketItemData
|
|
||||||
{
|
|
||||||
public string BasketItemId { get; set; }
|
|
||||||
|
|
||||||
public int NewQty { get; set; }
|
|
||||||
|
|
||||||
public UpdateBasketItemData()
|
|
||||||
{
|
|
||||||
NewQty = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class UpdateBasketItemsRequest
|
|
||||||
{
|
|
||||||
public string BasketId { get; set; }
|
|
||||||
|
|
||||||
public ICollection<UpdateBasketItemData> Updates { get; set; }
|
|
||||||
|
|
||||||
public UpdateBasketItemsRequest()
|
|
||||||
{
|
|
||||||
Updates = new List<UpdateBasketItemData>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
|
|
||||||
public class UpdateBasketRequest
|
|
||||||
{
|
|
||||||
public string BuyerId { get; set; }
|
|
||||||
|
|
||||||
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
|
|
||||||
{
|
|
||||||
public class UpdateBasketRequestItemData
|
|
||||||
{
|
|
||||||
public string Id { get; set; } // Basket id
|
|
||||||
|
|
||||||
public int ProductId { get; set; } // Catalog item id
|
|
||||||
|
|
||||||
public int Quantity { get; set; } // Quantity
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
using Microsoft.AspNetCore;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
BuildWebHost(args).Run();
|
|
||||||
|
|
||||||
IWebHost BuildWebHost(string[] args) =>
|
|
||||||
WebHost
|
|
||||||
.CreateDefaultBuilder(args)
|
|
||||||
.ConfigureAppConfiguration(cb =>
|
|
||||||
{
|
|
||||||
var sources = cb.Sources;
|
|
||||||
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource()
|
|
||||||
{
|
|
||||||
Optional = true,
|
|
||||||
Path = "appsettings.localhost.json",
|
|
||||||
ReloadOnChange = false
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.UseStartup<Startup>()
|
|
||||||
.UseSerilog((builderContext, config) =>
|
|
||||||
{
|
|
||||||
config
|
|
||||||
.MinimumLevel.Information()
|
|
||||||
.Enrich.FromLogContext()
|
|
||||||
.WriteTo.Console();
|
|
||||||
})
|
|
||||||
.Build();
|
|
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:57425/",
|
|
||||||
"sslPort": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "api/values",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PurchaseForMvc": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "api/values",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:61632/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
using GrpcBasket;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public class BasketService : IBasketService
|
|
||||||
{
|
|
||||||
private readonly Basket.BasketClient _basketClient;
|
|
||||||
private readonly ILogger<BasketService> _logger;
|
|
||||||
|
|
||||||
public BasketService(Basket.BasketClient basketClient, ILogger<BasketService> logger)
|
|
||||||
{
|
|
||||||
_basketClient = basketClient;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<BasketData> GetById(string id)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("grpc client created, request = {@id}", id);
|
|
||||||
var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id });
|
|
||||||
_logger.LogDebug("grpc response {@response}", response);
|
|
||||||
|
|
||||||
return MapToBasketData(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpdateAsync(BasketData currentBasket)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
|
|
||||||
var request = MapToCustomerBasketRequest(currentBasket);
|
|
||||||
_logger.LogDebug("Grpc update basket request {@request}", request);
|
|
||||||
|
|
||||||
await _basketClient.UpdateBasketAsync(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest)
|
|
||||||
{
|
|
||||||
if (customerBasketRequest == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var map = new BasketData
|
|
||||||
{
|
|
||||||
BuyerId = customerBasketRequest.Buyerid
|
|
||||||
};
|
|
||||||
|
|
||||||
customerBasketRequest.Items.ToList().ForEach(item =>
|
|
||||||
{
|
|
||||||
if (item.Id != null)
|
|
||||||
{
|
|
||||||
map.Items.Add(new BasketDataItem
|
|
||||||
{
|
|
||||||
Id = item.Id,
|
|
||||||
OldUnitPrice = (decimal)item.Oldunitprice,
|
|
||||||
PictureUrl = item.Pictureurl,
|
|
||||||
ProductId = item.Productid,
|
|
||||||
ProductName = item.Productname,
|
|
||||||
Quantity = item.Quantity,
|
|
||||||
UnitPrice = (decimal)item.Unitprice
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData)
|
|
||||||
{
|
|
||||||
if (basketData == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var map = new CustomerBasketRequest
|
|
||||||
{
|
|
||||||
Buyerid = basketData.BuyerId
|
|
||||||
};
|
|
||||||
|
|
||||||
basketData.Items.ToList().ForEach(item =>
|
|
||||||
{
|
|
||||||
if (item.Id != null)
|
|
||||||
{
|
|
||||||
map.Items.Add(new BasketItemResponse
|
|
||||||
{
|
|
||||||
Id = item.Id,
|
|
||||||
Oldunitprice = (double)item.OldUnitPrice,
|
|
||||||
Pictureurl = item.PictureUrl,
|
|
||||||
Productid = item.ProductId,
|
|
||||||
Productname = item.ProductName,
|
|
||||||
Quantity = item.Quantity,
|
|
||||||
Unitprice = (double)item.UnitPrice
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
using CatalogApi;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public class CatalogService : ICatalogService
|
|
||||||
{
|
|
||||||
private readonly Catalog.CatalogClient _client;
|
|
||||||
private readonly ILogger<CatalogService> _logger;
|
|
||||||
|
|
||||||
public CatalogService(Catalog.CatalogClient client, ILogger<CatalogService> logger)
|
|
||||||
{
|
|
||||||
_client = client;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<CatalogItem> GetCatalogItemAsync(int id)
|
|
||||||
{
|
|
||||||
var request = new CatalogItemRequest { Id = id };
|
|
||||||
_logger.LogInformation("grpc request {@request}", request);
|
|
||||||
var response = await _client.GetItemByIdAsync(request);
|
|
||||||
_logger.LogInformation("grpc response {@response}", response);
|
|
||||||
return MapToCatalogItemResponse(response);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
|
|
||||||
{
|
|
||||||
var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 };
|
|
||||||
_logger.LogInformation("grpc request {@request}", request);
|
|
||||||
var response = await _client.GetItemsByIdsAsync(request);
|
|
||||||
_logger.LogInformation("grpc response {@response}", response);
|
|
||||||
return response.Data.Select(this.MapToCatalogItemResponse);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse)
|
|
||||||
{
|
|
||||||
return new CatalogItem
|
|
||||||
{
|
|
||||||
Id = catalogItemResponse.Id,
|
|
||||||
Name = catalogItemResponse.Name,
|
|
||||||
PictureUri = catalogItemResponse.PictureUri,
|
|
||||||
Price = (decimal)catalogItemResponse.Price
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public interface IBasketService
|
|
||||||
{
|
|
||||||
Task<BasketData> GetById(string id);
|
|
||||||
|
|
||||||
Task UpdateAsync(BasketData currentBasket);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public interface ICatalogService
|
|
||||||
{
|
|
||||||
Task<CatalogItem> GetCatalogItemAsync(int id);
|
|
||||||
|
|
||||||
Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public interface IOrderApiClient
|
|
||||||
{
|
|
||||||
Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public interface IOrderingService
|
|
||||||
{
|
|
||||||
Task<OrderData> GetOrderDraftAsync(BasketData basketData);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public class OrderApiClient : IOrderApiClient
|
|
||||||
{
|
|
||||||
private readonly HttpClient _apiClient;
|
|
||||||
private readonly ILogger<OrderApiClient> _logger;
|
|
||||||
private readonly UrlsConfig _urls;
|
|
||||||
|
|
||||||
public OrderApiClient(HttpClient httpClient, ILogger<OrderApiClient> logger, IOptions<UrlsConfig> config)
|
|
||||||
{
|
|
||||||
_apiClient = httpClient;
|
|
||||||
_logger = logger;
|
|
||||||
_urls = config.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket)
|
|
||||||
{
|
|
||||||
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
|
|
||||||
var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json");
|
|
||||||
var response = await _apiClient.PostAsync(url, content);
|
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
|
|
||||||
var ordersDraftResponse = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
return JsonSerializer.Deserialize<OrderData>(ordersDraftResponse, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
using GrpcOrdering;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|
||||||
{
|
|
||||||
public class OrderingService : IOrderingService
|
|
||||||
{
|
|
||||||
private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
|
|
||||||
private readonly ILogger<OrderingService> _logger;
|
|
||||||
|
|
||||||
public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
|
|
||||||
{
|
|
||||||
_orderingGrpcClient = orderingGrpcClient;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OrderData> GetOrderDraftAsync(BasketData basketData)
|
|
||||||
{
|
|
||||||
_logger.LogDebug(" grpc client created, basketData={@basketData}", basketData);
|
|
||||||
|
|
||||||
var command = MapToOrderDraftCommand(basketData);
|
|
||||||
var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command);
|
|
||||||
_logger.LogDebug(" grpc response: {@response}", response);
|
|
||||||
|
|
||||||
return MapToResponse(response, basketData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData)
|
|
||||||
{
|
|
||||||
if (orderDraft == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = new OrderData
|
|
||||||
{
|
|
||||||
Buyer = basketData.BuyerId,
|
|
||||||
Total = (decimal)orderDraft.Total,
|
|
||||||
};
|
|
||||||
|
|
||||||
orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData
|
|
||||||
{
|
|
||||||
Discount = (decimal)o.Discount,
|
|
||||||
PictureUrl = o.PictureUrl,
|
|
||||||
ProductId = o.ProductId,
|
|
||||||
ProductName = o.ProductName,
|
|
||||||
UnitPrice = (decimal)o.UnitPrice,
|
|
||||||
Units = o.Units,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
|
|
||||||
{
|
|
||||||
var command = new CreateOrderDraftCommand
|
|
||||||
{
|
|
||||||
BuyerId = basketData.BuyerId,
|
|
||||||
};
|
|
||||||
|
|
||||||
basketData.Items.ForEach(i => command.Items.Add(new BasketItem
|
|
||||||
{
|
|
||||||
Id = i.Id,
|
|
||||||
OldUnitPrice = (double)i.OldUnitPrice,
|
|
||||||
PictureUrl = i.PictureUrl,
|
|
||||||
ProductId = i.ProductId,
|
|
||||||
ProductName = i.ProductName,
|
|
||||||
Quantity = i.Quantity,
|
|
||||||
UnitPrice = (double)i.UnitPrice,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,225 +0,0 @@
|
|||||||
using CatalogApi;
|
|
||||||
using Devspaces.Support;
|
|
||||||
using GrpcBasket;
|
|
||||||
using GrpcOrdering;
|
|
||||||
using HealthChecks.UI.Client;
|
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure;
|
|
||||||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
|
|
||||||
{
|
|
||||||
public class Startup
|
|
||||||
{
|
|
||||||
public Startup(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
Configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IConfiguration Configuration { get; }
|
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddHealthChecks()
|
|
||||||
.AddCheck("self", () => HealthCheckResult.Healthy())
|
|
||||||
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
|
|
||||||
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
|
|
||||||
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" })
|
|
||||||
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
|
|
||||||
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" });
|
|
||||||
|
|
||||||
services.AddCustomMvc(Configuration)
|
|
||||||
.AddCustomAuthentication(Configuration)
|
|
||||||
.AddDevspaces()
|
|
||||||
.AddApplicationServices()
|
|
||||||
.AddGrpcServices();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
||||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
|
|
||||||
{
|
|
||||||
var pathBase = Configuration["PATH_BASE"];
|
|
||||||
if (!string.IsNullOrEmpty(pathBase))
|
|
||||||
{
|
|
||||||
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
|
|
||||||
app.UsePathBase(pathBase);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseDeveloperExceptionPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
|
|
||||||
app.UseSwagger().UseSwaggerUI(c =>
|
|
||||||
{
|
|
||||||
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
|
|
||||||
|
|
||||||
c.OAuthClientId("webshoppingaggswaggerui");
|
|
||||||
c.OAuthClientSecret(string.Empty);
|
|
||||||
c.OAuthRealm(string.Empty);
|
|
||||||
c.OAuthAppName("web shopping bff Swagger UI");
|
|
||||||
});
|
|
||||||
|
|
||||||
app.UseRouting();
|
|
||||||
app.UseCors("CorsPolicy");
|
|
||||||
app.UseAuthentication();
|
|
||||||
app.UseAuthorization();
|
|
||||||
|
|
||||||
app.UseEndpoints(endpoints =>
|
|
||||||
{
|
|
||||||
endpoints.MapDefaultControllerRoute();
|
|
||||||
endpoints.MapControllers();
|
|
||||||
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
|
|
||||||
{
|
|
||||||
Predicate = _ => true,
|
|
||||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
|
||||||
});
|
|
||||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
|
||||||
{
|
|
||||||
Predicate = r => r.Name.Contains("self")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
|
|
||||||
|
|
||||||
var identityUrl = configuration.GetValue<string>("urls:identity");
|
|
||||||
services.AddAuthentication(options =>
|
|
||||||
{
|
|
||||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
||||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
||||||
|
|
||||||
})
|
|
||||||
.AddJwtBearer(options =>
|
|
||||||
{
|
|
||||||
options.Authority = identityUrl;
|
|
||||||
options.RequireHttpsMetadata = false;
|
|
||||||
options.Audience = "webshoppingagg";
|
|
||||||
});
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.AddOptions();
|
|
||||||
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
|
|
||||||
|
|
||||||
services.AddControllers()
|
|
||||||
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
|
|
||||||
|
|
||||||
services.AddSwaggerGen(options =>
|
|
||||||
{
|
|
||||||
options.DescribeAllEnumsAsStrings();
|
|
||||||
|
|
||||||
options.SwaggerDoc("v1", new OpenApiInfo
|
|
||||||
{
|
|
||||||
Title = "Shopping Aggregator for Web Clients",
|
|
||||||
Version = "v1",
|
|
||||||
Description = "Shopping Aggregator for Web Clients"
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Type = SecuritySchemeType.OAuth2,
|
|
||||||
Flows = new OpenApiOAuthFlows()
|
|
||||||
{
|
|
||||||
Implicit = new OpenApiOAuthFlow()
|
|
||||||
{
|
|
||||||
AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"),
|
|
||||||
TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"),
|
|
||||||
|
|
||||||
Scopes = new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{ "webshoppingagg", "Shopping Aggregator for Web Clients" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
options.OperationFilter<AuthorizeCheckOperationFilter>();
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddCors(options =>
|
|
||||||
{
|
|
||||||
options.AddPolicy("CorsPolicy",
|
|
||||||
builder => builder
|
|
||||||
.SetIsOriginAllowed((host) => true)
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowCredentials());
|
|
||||||
});
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
//register delegating handlers
|
|
||||||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
|
|
||||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
|
||||||
|
|
||||||
//register http services
|
|
||||||
|
|
||||||
services.AddHttpClient<IOrderApiClient, OrderApiClient>()
|
|
||||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
|
||||||
.AddDevspacesSupport();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddGrpcServices(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddTransient<GrpcExceptionInterceptor>();
|
|
||||||
|
|
||||||
services.AddScoped<IBasketService, BasketService>();
|
|
||||||
|
|
||||||
services.AddGrpcClient<Basket.BasketClient>((services, options) =>
|
|
||||||
{
|
|
||||||
var basketApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcBasket;
|
|
||||||
options.Address = new Uri(basketApi);
|
|
||||||
}).AddInterceptor<GrpcExceptionInterceptor>();
|
|
||||||
|
|
||||||
services.AddScoped<ICatalogService, CatalogService>();
|
|
||||||
|
|
||||||
services.AddGrpcClient<Catalog.CatalogClient>((services, options) =>
|
|
||||||
{
|
|
||||||
var catalogApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcCatalog;
|
|
||||||
options.Address = new Uri(catalogApi);
|
|
||||||
}).AddInterceptor<GrpcExceptionInterceptor>();
|
|
||||||
|
|
||||||
services.AddScoped<IOrderingService, OrderingService>();
|
|
||||||
|
|
||||||
services.AddGrpcClient<OrderingGrpc.OrderingGrpcClient>((services, options) =>
|
|
||||||
{
|
|
||||||
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering;
|
|
||||||
options.Address = new Uri(orderingApi);
|
|
||||||
}).AddInterceptor<GrpcExceptionInterceptor>();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
|
||||||
<AssemblyName>Web.Shopping.HttpAggregator</AssemblyName>
|
|
||||||
<RootNamespace>Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator</RootNamespace>
|
|
||||||
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
|
||||||
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
|
|
||||||
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
|
|
||||||
<LangVersion>preview</LangVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="wwwroot\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="5.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="5.0.1" />
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.14.0" />
|
|
||||||
<PackageReference Include="Grpc.AspNetCore.Server.ClientFactory" Version="2.34.0" />
|
|
||||||
<PackageReference Include="Grpc.Core" Version="2.34.0" />
|
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.34.0" />
|
|
||||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.34.0" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.34.0" PrivateAssets="All" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.2" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.2" />
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
|
||||||
<!--<PackageReference Include="System.Net.Http.WinHttpHandler" Version="4.6.0-rc1.19456.4" />-->
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\Devspaces.Support\Devspaces.Support.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Protobuf Include="..\..\..\Services\Basket\Basket.API\Proto\basket.proto" GrpcServices="Client" />
|
|
||||||
<Protobuf Include="..\..\..\Services\Catalog\Catalog.API\Proto\catalog.proto" GrpcServices="Client" />
|
|
||||||
<Protobuf Include="..\..\..\Services\Ordering\Ordering.API\Proto\ordering.proto" GrpcServices="Client" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"IncludeScopes": false,
|
|
||||||
"Debug": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Debug"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Console": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Debug"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"IncludeScopes": false,
|
|
||||||
"Debug": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Console": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"urls": {
|
|
||||||
"basket": "http://localhost:55105",
|
|
||||||
"catalog": "http://localhost:55101",
|
|
||||||
"orders": "http://localhost:55102",
|
|
||||||
"identity": "http://localhost:55105",
|
|
||||||
"grpcBasket": "http://localhost:5580",
|
|
||||||
"grpcCatalog": "http://localhost:81",
|
|
||||||
"grpcOrdering": "http://localhost:5581"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
kind: helm-release
|
|
||||||
apiVersion: 1.1
|
|
||||||
build:
|
|
||||||
context: ..\..\..\..
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
install:
|
|
||||||
chart: ../../../../k8s/helm/webshoppingagg
|
|
||||||
set:
|
|
||||||
image:
|
|
||||||
tag: $(tag)
|
|
||||||
pullPolicy: Never
|
|
||||||
ingress:
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: traefik-azds
|
|
||||||
hosts:
|
|
||||||
# This expands to [space.s.]apigwms.<guid>.<region>.aksapp.io
|
|
||||||
- $(spacePrefix)eshop$(hostSuffix)
|
|
||||||
inf:
|
|
||||||
k8s:
|
|
||||||
dns: $(spacePrefix)eshop$(hostSuffix)
|
|
||||||
values:
|
|
||||||
- values.dev.yaml?
|
|
||||||
- secrets.dev.yaml?
|
|
||||||
- app.yaml
|
|
||||||
- inf.yaml
|
|
||||||
configurations:
|
|
||||||
develop:
|
|
||||||
build:
|
|
||||||
useGitIgnore: true
|
|
||||||
dockerfile: Dockerfile.develop
|
|
||||||
container:
|
|
||||||
syncTarget: /src
|
|
||||||
sync:
|
|
||||||
- '**/Pages/**'
|
|
||||||
- '**/Views/**'
|
|
||||||
- '**/wwwroot/**'
|
|
||||||
- '!**/*.{sln,csproj}'
|
|
||||||
command:
|
|
||||||
- dotnet
|
|
||||||
- run
|
|
||||||
- --no-restore
|
|
||||||
- --no-build
|
|
||||||
- --no-launch-profile
|
|
||||||
- -c
|
|
||||||
- ${Configuration:-Debug}
|
|
||||||
iterate:
|
|
||||||
processesToKill:
|
|
||||||
- dotnet
|
|
||||||
- vsdbg
|
|
||||||
buildCommands:
|
|
||||||
- - dotnet
|
|
||||||
- build
|
|
||||||
- --no-restore
|
|
||||||
- -c
|
|
||||||
- ${Configuration:-Debug}
|
|
@ -1,2 +0,0 @@
|
|||||||
ocelot:
|
|
||||||
configPath: /app/configuration
|
|
@ -1,11 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
@ -1,29 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Devspaces.Support
|
|
||||||
{
|
|
||||||
public class DevspacesMessageHandler : DelegatingHandler
|
|
||||||
{
|
|
||||||
private const string DevspacesHeaderName = "azds-route-as";
|
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
||||||
public DevspacesMessageHandler(IHttpContextAccessor httpContextAccessor)
|
|
||||||
{
|
|
||||||
_httpContextAccessor = httpContextAccessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var req = _httpContextAccessor.HttpContext.Request;
|
|
||||||
|
|
||||||
if (req.Headers.ContainsKey(DevspacesHeaderName))
|
|
||||||
{
|
|
||||||
request.Headers.Add(DevspacesHeaderName, req.Headers[DevspacesHeaderName] as IEnumerable<string>);
|
|
||||||
}
|
|
||||||
return base.SendAsync(request, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Devspaces.Support
|
|
||||||
{
|
|
||||||
public static class HttpClientBuilderDevspacesExtensions
|
|
||||||
{
|
|
||||||
public static IHttpClientBuilder AddDevspacesSupport(this IHttpClientBuilder builder)
|
|
||||||
{
|
|
||||||
builder.AddHttpMessageHandler<DevspacesMessageHandler>();
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Devspaces.Support
|
|
||||||
{
|
|
||||||
public static class ServiceCollectionDevspacesExtensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddDevspaces(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddTransient<DevspacesMessageHandler>();
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,2 +0,0 @@
|
|||||||
# eShopOnContainers
|
|
||||||
The content of this folder has been moved to the repository - [eshop-mobile-client](https://github.com/dotnet-architecture/eshop-mobile-client)
|
|
@ -1,14 +0,0 @@
|
|||||||
.dockerignore
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.vs
|
|
||||||
.vscode
|
|
||||||
**/*.*proj.user
|
|
||||||
**/azds.yaml
|
|
||||||
**/bin
|
|
||||||
**/charts
|
|
||||||
**/Dockerfile
|
|
||||||
**/Dockerfile.develop
|
|
||||||
**/obj
|
|
||||||
**/secrets.dev.yaml
|
|
||||||
**/values.dev.yaml
|
|
@ -1,28 +0,0 @@
|
|||||||
(function ($, swaggerUi) {
|
|
||||||
$(function () {
|
|
||||||
var settings = {
|
|
||||||
authority: 'https://localhost:5105',
|
|
||||||
client_id: 'js',
|
|
||||||
popup_redirect_uri: window.location.protocol
|
|
||||||
+ '//'
|
|
||||||
+ window.location.host
|
|
||||||
+ '/tokenclient/popup.html',
|
|
||||||
|
|
||||||
response_type: 'id_token token',
|
|
||||||
scope: 'openid profile basket',
|
|
||||||
|
|
||||||
filter_protocol_claims: true
|
|
||||||
},
|
|
||||||
manager = new OidcTokenManager(settings),
|
|
||||||
$inputApiKey = $('#input_apiKey');
|
|
||||||
|
|
||||||
$inputApiKey.on('dblclick', function () {
|
|
||||||
manager.openPopupForTokenAsync()
|
|
||||||
.then(function () {
|
|
||||||
$inputApiKey.val(manager.access_token).change();
|
|
||||||
}, function (error) {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(jQuery, window.swaggerUi);
|
|
File diff suppressed because one or more lines are too long
@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title></title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script type="text/javascript" src="oidc-token-manager.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
new OidcTokenManager().processTokenPopup();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,33 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server
|
|
||||||
{
|
|
||||||
public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
|
|
||||||
{
|
|
||||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
|
||||||
{
|
|
||||||
var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
|
|
||||||
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
|
|
||||||
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);
|
|
||||||
|
|
||||||
if (isAuthorized && !allowAnonymous)
|
|
||||||
{
|
|
||||||
if (operation.Parameters == null)
|
|
||||||
operation.Parameters = new List<OpenApiParameter>();
|
|
||||||
|
|
||||||
|
|
||||||
operation.Parameters.Add(new OpenApiParameter
|
|
||||||
{
|
|
||||||
Name = "Authorization",
|
|
||||||
In = ParameterLocation.Header,
|
|
||||||
Description = "access token",
|
|
||||||
Required = true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
|
||||||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
|
|
||||||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
|
||||||
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
|
|
||||||
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
|
|
||||||
<LangVersion>preview</LangVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Update="web.config">
|
|
||||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="5.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="5.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="5.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="5.0.1" />
|
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.1.0" />
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.14.0" />
|
|
||||||
<PackageReference Include="Grpc.AspNetCore.Server" Version="2.34.0" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.34.0" PrivateAssets="All" />
|
|
||||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.16.0" />
|
|
||||||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.16.0" />
|
|
||||||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.1.3" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.2" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.11" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="5.0.2" />
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
|
||||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
|
|
||||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.1-dev-00216" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Http" Version="7.2.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Seq" Version="4.1.0-dev-00166" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Protobuf Include="Proto\basket.proto" GrpcServices="Server" Generator="MSBuild:Compile" />
|
|
||||||
<Content Include="@(Protobuf)" />
|
|
||||||
<None Remove="@(Protobuf)" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
|
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" />
|
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Services.Basket.API
|
|
||||||
{
|
|
||||||
public class BasketSettings
|
|
||||||
{
|
|
||||||
public string ConnectionString { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
using Basket.API.IntegrationEvents.Events;
|
|
||||||
using Basket.API.Model;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Basket.API.Services;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
|
|
||||||
{
|
|
||||||
[Route("api/v1/[controller]")]
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
public class BasketController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly IBasketRepository _repository;
|
|
||||||
private readonly IIdentityService _identityService;
|
|
||||||
private readonly IEventBus _eventBus;
|
|
||||||
private readonly ILogger<BasketController> _logger;
|
|
||||||
|
|
||||||
public BasketController(
|
|
||||||
ILogger<BasketController> logger,
|
|
||||||
IBasketRepository repository,
|
|
||||||
IIdentityService identityService,
|
|
||||||
IEventBus eventBus)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_repository = repository;
|
|
||||||
_identityService = identityService;
|
|
||||||
_eventBus = eventBus;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
|
||||||
[ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult<CustomerBasket>> GetBasketByIdAsync(string id)
|
|
||||||
{
|
|
||||||
var basket = await _repository.GetBasketAsync(id);
|
|
||||||
|
|
||||||
return Ok(basket ?? new CustomerBasket(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task<ActionResult<CustomerBasket>> UpdateBasketAsync([FromBody] CustomerBasket value)
|
|
||||||
{
|
|
||||||
return Ok(await _repository.UpdateBasketAsync(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("checkout")]
|
|
||||||
[HttpPost]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.Accepted)]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
public async Task<ActionResult> CheckoutAsync([FromBody] BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId)
|
|
||||||
{
|
|
||||||
var userId = _identityService.GetUserIdentity();
|
|
||||||
|
|
||||||
basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ?
|
|
||||||
guid : basketCheckout.RequestId;
|
|
||||||
|
|
||||||
var basket = await _repository.GetBasketAsync(userId);
|
|
||||||
|
|
||||||
if (basket == null)
|
|
||||||
{
|
|
||||||
return BadRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
var userName = this.HttpContext.User.FindFirst(x => x.Type == ClaimTypes.Name).Value;
|
|
||||||
|
|
||||||
var eventMessage = new UserCheckoutAcceptedIntegrationEvent(userId, userName, basketCheckout.City, basketCheckout.Street,
|
|
||||||
basketCheckout.State, basketCheckout.Country, basketCheckout.ZipCode, basketCheckout.CardNumber, basketCheckout.CardHolderName,
|
|
||||||
basketCheckout.CardExpiration, basketCheckout.CardSecurityNumber, basketCheckout.CardTypeId, basketCheckout.Buyer, basketCheckout.RequestId, basket);
|
|
||||||
|
|
||||||
// Once basket is checkout, sends an integration event to
|
|
||||||
// ordering.api to convert basket to order and proceeds with
|
|
||||||
// order creation process
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_eventBus.Publish(eventMessage);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName}", eventMessage.Id, Program.AppName);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Accepted();
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE api/values/5
|
|
||||||
[HttpDelete("{id}")]
|
|
||||||
[ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)]
|
|
||||||
public async Task DeleteBasketByIdAsync(string id)
|
|
||||||
{
|
|
||||||
await _repository.DeleteBasketAsync(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
|
|
||||||
{
|
|
||||||
public class HomeController : Controller
|
|
||||||
{
|
|
||||||
// GET: /<controller>/
|
|
||||||
public IActionResult Index()
|
|
||||||
{
|
|
||||||
return new RedirectResult("~/swagger");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
|
|
||||||
WORKDIR /app
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
|
||||||
WORKDIR /src
|
|
||||||
|
|
||||||
# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles
|
|
||||||
# to take advantage of Docker's build cache, to speed up local container builds
|
|
||||||
COPY "eShopOnContainers-ServicesAndWebApps.sln" "eShopOnContainers-ServicesAndWebApps.sln"
|
|
||||||
|
|
||||||
COPY "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj"
|
|
||||||
COPY "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj"
|
|
||||||
COPY "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj" "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBus/EventBus.csproj" "BuildingBlocks/EventBus/EventBus/EventBus.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj" "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj"
|
|
||||||
COPY "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj" "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj"
|
|
||||||
COPY "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj" "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj"
|
|
||||||
COPY "Services/Basket/Basket.API/Basket.API.csproj" "Services/Basket/Basket.API/Basket.API.csproj"
|
|
||||||
COPY "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj" "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj" "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.API/Catalog.API.csproj" "Services/Catalog/Catalog.API/Catalog.API.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj" "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj" "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj"
|
|
||||||
COPY "Services/Identity/Identity.API/Identity.API.csproj" "Services/Identity/Identity.API/Identity.API.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.API/Ordering.API.csproj" "Services/Ordering/Ordering.API/Ordering.API.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj" "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj" "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj" "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj" "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj"
|
|
||||||
COPY "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj"
|
|
||||||
COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment.API/Payment.API.csproj"
|
|
||||||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
|
|
||||||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
|
|
||||||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
|
|
||||||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj"
|
|
||||||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj"
|
|
||||||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
|
|
||||||
|
|
||||||
COPY "docker-compose.dcproj" "docker-compose.dcproj"
|
|
||||||
|
|
||||||
COPY "NuGet.config" "NuGet.config"
|
|
||||||
|
|
||||||
RUN dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
WORKDIR /src/Services/Basket/Basket.API
|
|
||||||
RUN dotnet publish --no-restore -c Release -o /app
|
|
||||||
|
|
||||||
FROM build as unittest
|
|
||||||
WORKDIR /src/Services/Basket/Basket.UnitTests
|
|
||||||
|
|
||||||
FROM build as functionaltest
|
|
||||||
WORKDIR /src/Services/Basket/Basket.FunctionalTests
|
|
||||||
|
|
||||||
FROM build AS publish
|
|
||||||
|
|
||||||
FROM base AS final
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=publish /app .
|
|
||||||
ENTRYPOINT ["dotnet", "Basket.API.dll"]
|
|
@ -1,20 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0
|
|
||||||
ARG BUILD_CONFIGURATION=Debug
|
|
||||||
ENV ASPNETCORE_ENVIRONMENT=Development
|
|
||||||
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
WORKDIR /src
|
|
||||||
|
|
||||||
COPY ["BuildingBlocks/EventBus/EventBus/EventBus.csproj", "BuildingBlocks/EventBus/EventBus/"]
|
|
||||||
COPY ["BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj", "BuildingBlocks/EventBus/EventBusRabbitMQ/"]
|
|
||||||
COPY ["BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj", "BuildingBlocks/EventBus/EventBusServiceBus/"]
|
|
||||||
COPY ["Services/Basket/Basket.API/Basket.API.csproj", "Services/Basket/Basket.API/"]
|
|
||||||
COPY ["NuGet.config", "NuGet.config"]
|
|
||||||
|
|
||||||
RUN dotnet restore Services/Basket/Basket.API/Basket.API.csproj -nowarn:msb3202,nu1503
|
|
||||||
COPY . .
|
|
||||||
WORKDIR /src/Services/Basket/Basket.API
|
|
||||||
RUN dotnet build -c $BUILD_CONFIGURATION
|
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet", "run", "--no-build", "--no-launch-profile", "-c", "$BUILD_CONFIGURATION", "--"]
|
|
@ -1,102 +0,0 @@
|
|||||||
using Grpc.Core;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace GrpcBasket
|
|
||||||
{
|
|
||||||
public class BasketService : Basket.BasketBase
|
|
||||||
{
|
|
||||||
private readonly IBasketRepository _repository;
|
|
||||||
private readonly ILogger<BasketService> _logger;
|
|
||||||
|
|
||||||
public BasketService(IBasketRepository repository, ILogger<BasketService> logger)
|
|
||||||
{
|
|
||||||
_repository = repository;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
[AllowAnonymous]
|
|
||||||
public override async Task<CustomerBasketResponse> GetBasketById(BasketRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Begin grpc call from method {Method} for basket id {Id}", context.Method, request.Id);
|
|
||||||
|
|
||||||
var data = await _repository.GetBasketAsync(request.Id);
|
|
||||||
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
context.Status = new Status(StatusCode.OK, $"Basket with id {request.Id} do exist");
|
|
||||||
|
|
||||||
return MapToCustomerBasketResponse(data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.Status = new Status(StatusCode.NotFound, $"Basket with id {request.Id} do not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CustomerBasketResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<CustomerBasketResponse> UpdateBasket(CustomerBasketRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Begin grpc call BasketService.UpdateBasketAsync for buyer id {Buyerid}", request.Buyerid);
|
|
||||||
|
|
||||||
var customerBasket = MapToCustomerBasket(request);
|
|
||||||
|
|
||||||
var response = await _repository.UpdateBasketAsync(customerBasket);
|
|
||||||
|
|
||||||
if (response != null)
|
|
||||||
{
|
|
||||||
return MapToCustomerBasketResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Status = new Status(StatusCode.NotFound, $"Basket with buyer id {request.Buyerid} do not exist");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CustomerBasketResponse MapToCustomerBasketResponse(CustomerBasket customerBasket)
|
|
||||||
{
|
|
||||||
var response = new CustomerBasketResponse
|
|
||||||
{
|
|
||||||
Buyerid = customerBasket.BuyerId
|
|
||||||
};
|
|
||||||
|
|
||||||
customerBasket.Items.ForEach(item => response.Items.Add(new BasketItemResponse
|
|
||||||
{
|
|
||||||
Id = item.Id,
|
|
||||||
Oldunitprice = (double)item.OldUnitPrice,
|
|
||||||
Pictureurl = item.PictureUrl,
|
|
||||||
Productid = item.ProductId,
|
|
||||||
Productname = item.ProductName,
|
|
||||||
Quantity = item.Quantity,
|
|
||||||
Unitprice = (double)item.UnitPrice
|
|
||||||
}));
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CustomerBasket MapToCustomerBasket(CustomerBasketRequest customerBasketRequest)
|
|
||||||
{
|
|
||||||
var response = new CustomerBasket
|
|
||||||
{
|
|
||||||
BuyerId = customerBasketRequest.Buyerid
|
|
||||||
};
|
|
||||||
|
|
||||||
customerBasketRequest.Items.ToList().ForEach(item => response.Items.Add(new BasketItem
|
|
||||||
{
|
|
||||||
Id = item.Id,
|
|
||||||
OldUnitPrice = (decimal)item.Oldunitprice,
|
|
||||||
PictureUrl = item.Pictureurl,
|
|
||||||
ProductId = item.Productid,
|
|
||||||
ProductName = item.Productname,
|
|
||||||
Quantity = item.Quantity,
|
|
||||||
UnitPrice = (decimal)item.Unitprice
|
|
||||||
}));
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Basket.API.Infrastructure.ActionResults
|
|
||||||
{
|
|
||||||
public class InternalServerErrorObjectResult : ObjectResult
|
|
||||||
{
|
|
||||||
public InternalServerErrorObjectResult(object error)
|
|
||||||
: base(error)
|
|
||||||
{
|
|
||||||
StatusCode = StatusCodes.Status500InternalServerError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Basket.API.Infrastructure.Exceptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Exception type for app exceptions
|
|
||||||
/// </summary>
|
|
||||||
public class BasketDomainException : Exception
|
|
||||||
{
|
|
||||||
public BasketDomainException()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public BasketDomainException(string message)
|
|
||||||
: base(message)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public BasketDomainException(string message, Exception innerException)
|
|
||||||
: base(message, innerException)
|
|
||||||
{ }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Basket.API.Infrastructure.Middlewares
|
|
||||||
{
|
|
||||||
public static class FailingMiddlewareAppBuilderExtensions
|
|
||||||
{
|
|
||||||
public static IApplicationBuilder UseFailingMiddleware(this IApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
return UseFailingMiddleware(builder, null);
|
|
||||||
}
|
|
||||||
public static IApplicationBuilder UseFailingMiddleware(this IApplicationBuilder builder, Action<FailingOptions> action)
|
|
||||||
{
|
|
||||||
var options = new FailingOptions();
|
|
||||||
action?.Invoke(options);
|
|
||||||
builder.UseMiddleware<FailingMiddleware>(options);
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
using Basket.API.Infrastructure.ActionResults;
|
|
||||||
using Basket.API.Infrastructure.Exceptions;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Basket.API.Infrastructure.Filters
|
|
||||||
{
|
|
||||||
public partial class HttpGlobalExceptionFilter : IExceptionFilter
|
|
||||||
{
|
|
||||||
private readonly IWebHostEnvironment env;
|
|
||||||
private readonly ILogger<HttpGlobalExceptionFilter> logger;
|
|
||||||
|
|
||||||
public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger<HttpGlobalExceptionFilter> logger)
|
|
||||||
{
|
|
||||||
this.env = env;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnException(ExceptionContext context)
|
|
||||||
{
|
|
||||||
logger.LogError(new EventId(context.Exception.HResult),
|
|
||||||
context.Exception,
|
|
||||||
context.Exception.Message);
|
|
||||||
|
|
||||||
if (context.Exception.GetType() == typeof(BasketDomainException))
|
|
||||||
{
|
|
||||||
var json = new JsonErrorResponse
|
|
||||||
{
|
|
||||||
Messages = new[] { context.Exception.Message }
|
|
||||||
};
|
|
||||||
|
|
||||||
context.Result = new BadRequestObjectResult(json);
|
|
||||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var json = new JsonErrorResponse
|
|
||||||
{
|
|
||||||
Messages = new[] { "An error occurred. Try it again." }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
|
||||||
{
|
|
||||||
json.DeveloperMessage = context.Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Result = new InternalServerErrorObjectResult(json);
|
|
||||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
|
||||||
}
|
|
||||||
context.ExceptionHandled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace Basket.API.Infrastructure.Filters
|
|
||||||
{
|
|
||||||
public class JsonErrorResponse
|
|
||||||
{
|
|
||||||
public string[] Messages { get; set; }
|
|
||||||
|
|
||||||
public object DeveloperMessage { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user