├── src ├── ApiGateways │ ├── OcelotApiGw │ │ ├── ocelot.json │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── OcelotApiGw - Backup.csproj │ │ ├── OcelotApiGw.csproj │ │ ├── Dockerfile │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Program.cs │ │ └── Startup.cs │ └── Shopping.Aggregator │ │ ├── appsettings.Development.json │ │ ├── Services │ │ ├── IBasketService.cs │ │ ├── IOrderService.cs │ │ ├── ICatalogService.cs │ │ ├── BasketService.cs │ │ ├── OrderService.cs │ │ └── CatalogService.cs │ │ ├── Models │ │ ├── ShoppingModel.cs │ │ ├── BasketModel.cs │ │ ├── CatalogModel.cs │ │ ├── BasketItemExtendedModel.cs │ │ └── OrderResponseModel.cs │ │ ├── appsettings.json │ │ ├── Shopping.Aggregator.csproj │ │ ├── Extensions │ │ └── HttpClientExtensions.cs │ │ ├── Program.cs │ │ ├── Dockerfile │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Startup.cs │ │ └── Controllers │ │ └── ShoppingController.cs ├── WebApps │ └── AspnetRunBasics │ │ ├── Pages │ │ ├── _ViewStart.cshtml │ │ ├── Privacy.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Shared │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ ├── _TopProductPartial.cshtml │ │ │ └── _ProductItemPartial.cshtml │ │ ├── Contact.cshtml.cs │ │ ├── Confirmation.cshtml │ │ ├── Confirmation.cshtml.cs │ │ ├── Privacy.cshtml.cs │ │ ├── Order.cshtml.cs │ │ ├── Error.cshtml.cs │ │ ├── Error.cshtml │ │ ├── Cart.cshtml.cs │ │ ├── CheckOut.cshtml.cs │ │ ├── Index.cshtml.cs │ │ ├── ProductDetail.cshtml.cs │ │ ├── Order.cshtml │ │ ├── Product.cshtml.cs │ │ ├── Contact.cshtml │ │ ├── Index.cshtml │ │ ├── Product.cshtml │ │ └── Cart.cshtml │ │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── images │ │ │ ├── placeholder.png │ │ │ ├── banner │ │ │ │ ├── banner1.png │ │ │ │ ├── banner2.png │ │ │ │ └── banner3.png │ │ │ └── product │ │ │ │ ├── product-1.png │ │ │ │ ├── product-2.png │ │ │ │ ├── product-3.png │ │ │ │ ├── product-4.png │ │ │ │ ├── product-5.png │ │ │ │ ├── product-6.png │ │ │ │ ├── product-7.png │ │ │ │ ├── productx1.png │ │ │ │ ├── productx2.png │ │ │ │ ├── productx3.png │ │ │ │ ├── productx4.png │ │ │ │ ├── productx5.png │ │ │ │ ├── productx6.png │ │ │ │ └── productx7.png │ │ └── css │ │ │ └── style.css │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Services │ │ ├── IOrderService.cs │ │ ├── IBasketService.cs │ │ ├── ICatalogService.cs │ │ ├── OrderService.cs │ │ ├── BasketService.cs │ │ └── CatalogService.cs │ │ ├── Models │ │ ├── BasketModel.cs │ │ ├── BasketItemModel.cs │ │ ├── CatalogModel.cs │ │ ├── BasketCheckoutModel.cs │ │ └── OrderResponseModel.cs │ │ ├── Program.cs │ │ ├── AspnetRunBasics.csproj │ │ ├── Dockerfile │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Extensions │ │ └── HttpClientExtensions.cs │ │ └── Startup.cs ├── Services │ ├── Ordering │ │ ├── Ordering.Domain │ │ │ ├── Ordering.Domain.csproj │ │ │ ├── Common │ │ │ │ ├── EntityBase.cs │ │ │ │ └── ValueObject.cs │ │ │ └── Entities │ │ │ │ └── Order.cs │ │ ├── Ordering.API │ │ │ ├── appsettings.Development.json │ │ │ ├── Mapping │ │ │ │ └── OrderingProfile.cs │ │ │ ├── appsettings.json │ │ │ ├── Ordering - Backup.API.csproj │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Program.cs │ │ │ ├── Dockerfile │ │ │ ├── EventBusConsumer │ │ │ │ └── BasketCheckoutConsumer.cs │ │ │ ├── Ordering.API.csproj │ │ │ ├── Extensions │ │ │ │ └── HostExtensions.cs │ │ │ ├── Controllers │ │ │ │ └── OrderController.cs │ │ │ └── Startup.cs │ │ ├── Ordering.Application │ │ │ ├── Models │ │ │ │ ├── Email.cs │ │ │ │ └── EmailSettings.cs │ │ │ ├── Features │ │ │ │ └── Orders │ │ │ │ │ ├── Commands │ │ │ │ │ ├── DeleteOrder │ │ │ │ │ │ ├── DeleteOrderCommand.cs │ │ │ │ │ │ └── DeleteOrderCommandHandler.cs │ │ │ │ │ ├── UpdateOrder │ │ │ │ │ │ ├── UpdateOrderCommandValidator.cs │ │ │ │ │ │ ├── UpdateOrderCommand.cs │ │ │ │ │ │ └── UpdateOrderCommandHandler.cs │ │ │ │ │ └── CheckoutOrder │ │ │ │ │ │ ├── CheckoutOrderCommandValidator.cs │ │ │ │ │ │ ├── CheckoutOrderCommand.cs │ │ │ │ │ │ └── CheckoutOrderCommandHandler.cs │ │ │ │ │ └── Queries │ │ │ │ │ └── GetOrdersList │ │ │ │ │ ├── GetOrdersListQuery.cs │ │ │ │ │ ├── OrdersVm.cs │ │ │ │ │ └── GetOrdersListQueryHandler.cs │ │ │ ├── Contracts │ │ │ │ ├── Infrastructure │ │ │ │ │ └── IEmailService.cs │ │ │ │ └── Persistence │ │ │ │ │ ├── IOrderRepository.cs │ │ │ │ │ └── IAsyncRepository.cs │ │ │ ├── Exceptions │ │ │ │ ├── NotFoundException.cs │ │ │ │ └── ValidationException.cs │ │ │ ├── Mappings │ │ │ │ └── MappingProfile.cs │ │ │ ├── Ordering.Application.csproj │ │ │ ├── ApplicationServiceRegistration.cs │ │ │ └── Behaviours │ │ │ │ ├── UnhandledExceptionBehaviour.cs │ │ │ │ └── ValidationBehaviour.cs │ │ └── Ordering.Infrastructure │ │ │ ├── Ordering.Infrastructure.csproj │ │ │ ├── Repositories │ │ │ ├── OrderRepository.cs │ │ │ └── RepositoryBase.cs │ │ │ ├── Persistence │ │ │ ├── OrderContextSeed.cs │ │ │ └── OrderContext.cs │ │ │ ├── InfrastructureServiceRegistration.cs │ │ │ ├── Mail │ │ │ └── EmailService.cs │ │ │ └── Migrations │ │ │ └── 20210319130102_InitialCreate.cs │ ├── Basket │ │ └── Basket.API │ │ │ ├── appsettings.Development.json │ │ │ ├── Entities │ │ │ ├── ShoppingCartItem.cs │ │ │ ├── ShoppingCart.cs │ │ │ └── BasketCheckout.cs │ │ │ ├── Mapper │ │ │ └── BasketProfile.cs │ │ │ ├── Repositories │ │ │ ├── IBasketRepository.cs │ │ │ └── BasketRepository.cs │ │ │ ├── appsettings.json │ │ │ ├── Program.cs │ │ │ ├── Dockerfile │ │ │ ├── GrpcServices │ │ │ └── DiscountGrpcService.cs │ │ │ ├── Properties │ │ │ └── launchSettings.json │ │ │ ├── Basket.API.csproj │ │ │ └── Startup.cs │ ├── Discount │ │ ├── Discount.API │ │ │ ├── appsettings.Development.json │ │ │ ├── Entities │ │ │ │ └── Coupon.cs │ │ │ ├── appsettings.json │ │ │ ├── Repositories │ │ │ │ ├── IDiscountRepository.cs │ │ │ │ └── DiscountRepository.cs │ │ │ ├── Discount - Backup.API.csproj │ │ │ ├── Program.cs │ │ │ ├── Discount.API.csproj │ │ │ ├── Dockerfile │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Startup.cs │ │ │ ├── Controllers │ │ │ │ └── DiscountController.cs │ │ │ └── Extensions │ │ │ │ └── HostExtensions.cs │ │ └── Discount.Grpc │ │ │ ├── appsettings.Development.json │ │ │ ├── Entities │ │ │ └── Coupon.cs │ │ │ ├── Repositories │ │ │ ├── IDiscountRepository.cs │ │ │ └── DiscountRepository.cs │ │ │ ├── Mapper │ │ │ └── DiscountProfile.cs │ │ │ ├── appsettings.json │ │ │ ├── Properties │ │ │ └── launchSettings.json │ │ │ ├── Discount - Backup.Grpc.csproj │ │ │ ├── Dockerfile │ │ │ ├── Program.cs │ │ │ ├── Protos │ │ │ └── discount.proto │ │ │ ├── Discount.Grpc.csproj │ │ │ ├── Startup.cs │ │ │ ├── Extensions │ │ │ └── HostExtensions.cs │ │ │ └── Services │ │ │ └── DiscountService.cs │ └── Catalog │ │ └── Catalog.API │ │ ├── Data │ │ ├── ICatalogContext.cs │ │ └── CatalogContext.cs │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Repositories │ │ ├── IProductRepository.cs │ │ └── ProductRepository.cs │ │ ├── Catalog.API.csproj │ │ ├── Entities │ │ └── Product.cs │ │ ├── Program.cs │ │ ├── Dockerfile │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Startup.cs │ │ └── Controllers │ │ └── CatalogController.cs ├── BuildingBlocks │ └── EventBus.Messages │ │ ├── EventBus.Messages.csproj │ │ ├── Common │ │ └── EventBusConstants.cs │ │ └── Events │ │ ├── IntegrationBaseEvent.cs │ │ └── BasketCheckoutEvent.cs ├── .dockerignore ├── docker-compose.dcproj └── docker-compose.yml └── LICENSE /src/ApiGateways/OcelotApiGw/ocelot.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/placeholder.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/banner/banner1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/banner/banner1.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/banner/banner2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/banner/banner2.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/banner/banner3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/banner/banner3.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/product-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/product-1.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/product-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/product-2.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/product-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/product-3.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/product-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/product-4.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/product-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/product-5.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/product-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/product-6.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/product-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/product-7.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/productx1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/productx1.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/productx2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/productx2.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/productx3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/productx3.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/productx4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/productx4.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/productx5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/productx5.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/productx6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/productx6.png -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/images/product/productx7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehmetozkaya/AspnetMicroservices/HEAD/src/WebApps/AspnetRunBasics/wwwroot/images/product/productx7.png -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/EventBus.Messages/EventBus.Messages.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Use this page to detail your site's privacy policy.

9 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using AspnetRunBasics 2 | @using AspnetRunBasics.Models 3 | @namespace AspnetRunBasics.Pages 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | @addTagHelper *, AspnetRunBasics -------------------------------------------------------------------------------- /src/BuildingBlocks/EventBus.Messages/Common/EventBusConstants.cs: -------------------------------------------------------------------------------- 1 | namespace EventBus.Messages.Common 2 | { 3 | public static class EventBusConstants 4 | { 5 | public const string BasketCheckoutQueue = "basketcheckout-queue"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/ApiGateways/OcelotApiGw/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/ApiGateways/OcelotApiGw/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Grpc": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Data/ICatalogContext.cs: -------------------------------------------------------------------------------- 1 | using Catalog.API.Entities; 2 | using MongoDB.Driver; 3 | 4 | namespace Catalog.API.Data 5 | { 6 | public interface ICatalogContext 7 | { 8 | IMongoCollection Products { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Models/Email.cs: -------------------------------------------------------------------------------- 1 | namespace Ordering.Application.Models 2 | { 3 | public class Email 4 | { 5 | public string To { get; set; } 6 | public string Subject { get; set; } 7 | public string Body { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Services/IBasketService.cs: -------------------------------------------------------------------------------- 1 | using Shopping.Aggregator.Models; 2 | using System.Threading.Tasks; 3 | 4 | namespace Shopping.Aggregator.Services 5 | { 6 | public interface IBasketService 7 | { 8 | Task GetBasket(string userName); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Commands/DeleteOrder/DeleteOrderCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace Ordering.Application.Features.Orders.Commands.DeleteOrder 4 | { 5 | public class DeleteOrderCommand : IRequest 6 | { 7 | public int Id { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Models/EmailSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Ordering.Application.Models 2 | { 3 | public class EmailSettings 4 | { 5 | public string ApiKey { get; set; } 6 | public string FromAddress { get; set; } 7 | public string FromName { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Entities/Coupon.cs: -------------------------------------------------------------------------------- 1 | namespace Discount.API.Entities 2 | { 3 | public class Coupon 4 | { 5 | public int Id { get; set; } 6 | public string ProductName { get; set; } 7 | public string Description { get; set; } 8 | public int Amount { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Entities/Coupon.cs: -------------------------------------------------------------------------------- 1 | namespace Discount.Grpc.Entities 2 | { 3 | public class Coupon 4 | { 5 | public int Id { get; set; } 6 | public string ProductName { get; set; } 7 | public string Description { get; set; } 8 | public int Amount { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Contracts/Infrastructure/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using Ordering.Application.Models; 2 | using System.Threading.Tasks; 3 | 4 | namespace Ordering.Application.Contracts.Infrastructure 5 | { 6 | public interface IEmailService 7 | { 8 | Task SendEmail(Email email); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApiSettings": { 3 | "GatewayAddress": "http://localhost:8010" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Information", 8 | "Microsoft": "Warning", 9 | "Microsoft.Hosting.Lifetime": "Information" 10 | } 11 | }, 12 | "AllowedHosts": "*" 13 | } 14 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Services/IOrderService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRunBasics.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace AspnetRunBasics.Services 6 | { 7 | public interface IOrderService 8 | { 9 | Task> GetOrdersByUserName(string userName); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Services/IOrderService.cs: -------------------------------------------------------------------------------- 1 | using Shopping.Aggregator.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Shopping.Aggregator.Services 6 | { 7 | public interface IOrderService 8 | { 9 | Task> GetOrdersByUserName(string userName); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Models/BasketModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AspnetRunBasics.Models 4 | { 5 | public class BasketModel 6 | { 7 | public string UserName { get; set; } 8 | public List Items { get; set; } = new List(); 9 | public decimal TotalPrice { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Models/ShoppingModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Shopping.Aggregator.Models 4 | { 5 | public class ShoppingModel 6 | { 7 | public string UserName { get; set; } 8 | public BasketModel BasketWithProducts { get; set; } 9 | public IEnumerable Orders { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Ordering.Application.Exceptions 4 | { 5 | public class NotFoundException : ApplicationException 6 | { 7 | public NotFoundException(string name, object key) 8 | : base($"Entity \"{name}\" ({key}) was not found.") 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Models/BasketItemModel.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRunBasics.Models 2 | { 3 | public class BasketItemModel 4 | { 5 | public int Quantity { get; set; } 6 | public string Color { get; set; } 7 | public decimal Price { get; set; } 8 | public string ProductId { get; set; } 9 | public string ProductName { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Entities/ShoppingCartItem.cs: -------------------------------------------------------------------------------- 1 | namespace Basket.API.Entities 2 | { 3 | public class ShoppingCartItem 4 | { 5 | public int Quantity { get; set; } 6 | public string Color { get; set; } 7 | public decimal Price { get; set; } 8 | public string ProductId { get; set; } 9 | public string ProductName { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Mapper/BasketProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Basket.API.Entities; 3 | using EventBus.Messages.Events; 4 | 5 | namespace Basket.API.Mapper 6 | { 7 | public class BasketProfile : Profile 8 | { 9 | public BasketProfile() 10 | { 11 | CreateMap().ReverseMap(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Models/BasketModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Shopping.Aggregator.Models 4 | { 5 | public class BasketModel 6 | { 7 | public string UserName { get; set; } 8 | public List Items { get; set; } = new List(); 9 | public decimal TotalPrice { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Repositories/IBasketRepository.cs: -------------------------------------------------------------------------------- 1 | using Basket.API.Entities; 2 | using System.Threading.Tasks; 3 | 4 | namespace Basket.API.Repositories 5 | { 6 | public interface IBasketRepository 7 | { 8 | Task GetBasket(string userName); 9 | Task UpdateBasket(ShoppingCart basket); 10 | Task DeleteBasket(string userName); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Services/IBasketService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRunBasics.Models; 2 | using System.Threading.Tasks; 3 | 4 | namespace AspnetRunBasics.Services 5 | { 6 | public interface IBasketService 7 | { 8 | Task GetBasket(string userName); 9 | Task UpdateBasket(BasketModel model); 10 | Task CheckoutBasket(BasketCheckoutModel model); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "DatabaseSettings": { 3 | "ConnectionString": "Server=localhost;Port=5432;Database=DiscountDb;User Id=admin;Password=admin1234;" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Information", 8 | "Microsoft": "Warning", 9 | "Microsoft.Hosting.Lifetime": "Information" 10 | } 11 | }, 12 | "AllowedHosts": "*" 13 | } 14 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Contact.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace AspnetRunBasics 9 | { 10 | public class ContactModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DatabaseSettings": { 3 | "ConnectionString": "mongodb://catalogdb:27017", 4 | "DatabaseName": "ProductDb", 5 | "CollectionName": "Products" 6 | }, 7 | "Logging": { 8 | "LogLevel": { 9 | "Default": "Information", 10 | "Microsoft": "Warning", 11 | "Microsoft.Hosting.Lifetime": "Information" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Contracts/Persistence/IOrderRepository.cs: -------------------------------------------------------------------------------- 1 | using Ordering.Domain.Entities; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ordering.Application.Contracts.Persistence 6 | { 7 | public interface IOrderRepository : IAsyncRepository 8 | { 9 | Task> GetOrdersByUserName(string userName); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "DatabaseSettings": { 3 | "ConnectionString": "mongodb://localhost:27017", 4 | "DatabaseName": "ProductDb", 5 | "CollectionName": "Products" 6 | }, 7 | "Logging": { 8 | "LogLevel": { 9 | "Default": "Information", 10 | "Microsoft": "Warning", 11 | "Microsoft.Hosting.Lifetime": "Information" 12 | } 13 | }, 14 | "AllowedHosts": "*" 15 | } 16 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApiSettings": { 3 | "CatalogUrl": "http://localhost:8000", 4 | "BasketUrl": "http://localhost:8001", 5 | "OrderingUrl": "http://localhost:8004" 6 | }, 7 | "Logging": { 8 | "LogLevel": { 9 | "Default": "Information", 10 | "Microsoft": "Warning", 11 | "Microsoft.Hosting.Lifetime": "Information" 12 | } 13 | }, 14 | "AllowedHosts": "*" 15 | } 16 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Mapping/OrderingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using EventBus.Messages.Events; 3 | using Ordering.Application.Features.Orders.Commands.CheckoutOrder; 4 | 5 | namespace Ordering.API.Mapping 6 | { 7 | public class OrderingProfile : Profile 8 | { 9 | public OrderingProfile() 10 | { 11 | CreateMap().ReverseMap(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Domain/Common/EntityBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Ordering.Domain.Common 4 | { 5 | public abstract class EntityBase 6 | { 7 | public int Id { get; protected set; } 8 | public string CreatedBy { get; set; } 9 | public DateTime CreatedDate { get; set; } 10 | public string LastModifiedBy { get; set; } 11 | public DateTime? LastModifiedDate { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Services/ICatalogService.cs: -------------------------------------------------------------------------------- 1 | using Shopping.Aggregator.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Shopping.Aggregator.Services 6 | { 7 | public interface ICatalogService 8 | { 9 | Task> GetCatalog(); 10 | Task> GetCatalogByCategory(string category); 11 | Task GetCatalog(string id); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Models/CatalogModel.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRunBasics.Models 2 | { 3 | public class CatalogModel 4 | { 5 | public string Id { get; set; } 6 | public string Name { get; set; } 7 | public string Category { get; set; } 8 | public string Summary { get; set; } 9 | public string Description { get; set; } 10 | public string ImageFile { get; set; } 11 | public decimal Price { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ApiGateways/OcelotApiGw/OcelotApiGw - Backup.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\docker-compose.dcproj 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Repositories/IDiscountRepository.cs: -------------------------------------------------------------------------------- 1 | using Discount.API.Entities; 2 | using System.Threading.Tasks; 3 | 4 | namespace Discount.API.Repositories 5 | { 6 | public interface IDiscountRepository 7 | { 8 | Task GetDiscount(string productName); 9 | 10 | Task CreateDiscount(Coupon coupon); 11 | Task UpdateDiscount(Coupon coupon); 12 | Task DeleteDiscount(string productName); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Repositories/IDiscountRepository.cs: -------------------------------------------------------------------------------- 1 | using Discount.Grpc.Entities; 2 | using System.Threading.Tasks; 3 | 4 | namespace Discount.Grpc.Repositories 5 | { 6 | public interface IDiscountRepository 7 | { 8 | Task GetDiscount(string productName); 9 | 10 | Task CreateDiscount(Coupon coupon); 11 | Task UpdateDiscount(Coupon coupon); 12 | Task DeleteDiscount(string productName); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Models/CatalogModel.cs: -------------------------------------------------------------------------------- 1 | namespace Shopping.Aggregator.Models 2 | { 3 | public class CatalogModel 4 | { 5 | public string Id { get; set; } 6 | public string Name { get; set; } 7 | public string Category { get; set; } 8 | public string Summary { get; set; } 9 | public string Description { get; set; } 10 | public string ImageFile { get; set; } 11 | public decimal Price { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Mapper/DiscountProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Discount.Grpc.Entities; 3 | using Discount.Grpc.Protos; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace Discount.Grpc.Mapper 10 | { 11 | public class DiscountProfile : Profile 12 | { 13 | public DiscountProfile() 14 | { 15 | CreateMap().ReverseMap(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "DatabaseSettings": { 3 | "ConnectionString": "Server=localhost;Port=5432;Database=DiscountDb;User Id=admin;Password=admin1234;" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Information", 8 | "Microsoft": "Warning", 9 | "Microsoft.Hosting.Lifetime": "Information" 10 | } 11 | }, 12 | "AllowedHosts": "*", 13 | "Kestrel": { 14 | "EndpointDefaults": { 15 | "Protocols": "Http2" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "CacheSettings": { 3 | "ConnectionString": "localhost:6379" 4 | }, 5 | "GrpcSettings": { 6 | "DiscountUrl": "http://localhost:5003" 7 | }, 8 | "EventBusSettings": { 9 | "HostAddress": "amqp://guest:guest@localhost:5672" 10 | }, 11 | "Logging": { 12 | "LogLevel": { 13 | "Default": "Information", 14 | "Microsoft": "Warning", 15 | "Microsoft.Hosting.Lifetime": "Information" 16 | } 17 | }, 18 | "AllowedHosts": "*" 19 | } 20 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Discount.Grpc": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | }, 8 | "dotnetRunMessages": "true", 9 | "applicationUrl": "http://localhost:5003" 10 | }, 11 | "Docker": { 12 | "commandName": "Docker", 13 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 14 | "publishAllPorts": true 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Confirmation.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AspnetRunBasics.ConfirmationModel 3 | @{ 4 | ViewData["Title"] = "Confirmation"; 5 | } 6 | 7 |
8 |

9 | Confirmation 10 |

11 |
12 | @if (!string.IsNullOrEmpty(Model.Message)) 13 | { 14 |

@Model.Message

15 | } 16 |
17 |

If you have any further questions, you can contact us 501-222-2222.

18 |
19 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Confirmation.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | 3 | namespace AspnetRunBasics 4 | { 5 | public class ConfirmationModel : PageModel 6 | { 7 | public string Message { get; set; } 8 | 9 | public void OnGetContact() 10 | { 11 | Message = "Your email was sent."; 12 | } 13 | 14 | public void OnGetOrderSubmitted() 15 | { 16 | Message = "Your order submitted successfully."; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Services/ICatalogService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRunBasics.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace AspnetRunBasics.Services 6 | { 7 | public interface ICatalogService 8 | { 9 | Task> GetCatalog(); 10 | Task> GetCatalogByCategory(string category); 11 | Task GetCatalog(string id); 12 | Task CreateCatalog(CatalogModel model); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Discount - Backup.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\..\docker-compose.dcproj 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/BuildingBlocks/EventBus.Messages/Events/IntegrationBaseEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EventBus.Messages.Events 4 | { 5 | public class IntegrationBaseEvent 6 | { 7 | public IntegrationBaseEvent() 8 | { 9 | Id = Guid.NewGuid(); 10 | CreationDate = DateTime.UtcNow; 11 | } 12 | 13 | public IntegrationBaseEvent(Guid id, DateTime createDate) 14 | { 15 | Id = id; 16 | CreationDate = createDate; 17 | } 18 | 19 | public Guid Id { get; private set; } 20 | 21 | public DateTime CreationDate { get; private set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Queries/GetOrdersList/GetOrdersListQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Ordering.Application.Features.Orders.Queries.GetOrdersList 6 | { 7 | public class GetOrdersListQuery : IRequest> 8 | { 9 | public string UserName { get; set; } 10 | 11 | public GetOrdersListQuery(string userName) 12 | { 13 | UserName = userName ?? throw new ArgumentNullException(nameof(userName)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "OrderingConnectionString": "Server=localhost;Database=OrderDb;User Id=sa;Password=SwN12345678;" 4 | }, 5 | "EmailSettings": { 6 | "FromAddress": "ezozkme@gmail.com", 7 | "ApiKey": "", 8 | "FromName": "Mehmet" 9 | }, 10 | "EventBusSettings": { 11 | "HostAddress": "amqp://guest:guest@localhost:5672" 12 | }, 13 | "Logging": { 14 | "LogLevel": { 15 | "Default": "Information", 16 | "Microsoft": "Warning", 17 | "Microsoft.Hosting.Lifetime": "Information" 18 | } 19 | }, 20 | "AllowedHosts": "*" 21 | } 22 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace AspnetRunBasics 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/AspnetRunBasics.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | cd5124f0-00bb-4cff-b8b9-dc4e11396fa3 6 | Linux 7 | ..\..\docker-compose.dcproj 8 | ..\.. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Shopping.Aggregator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\docker-compose.dcproj 6 | Linux 7 | ..\.. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace AspnetRunBasics.Pages 10 | { 11 | public class PrivacyModel : PageModel 12 | { 13 | private readonly ILogger _logger; 14 | 15 | public PrivacyModel(ILogger logger) 16 | { 17 | _logger = logger; 18 | } 19 | 20 | public void OnGet() 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Models/BasketItemExtendedModel.cs: -------------------------------------------------------------------------------- 1 | namespace Shopping.Aggregator.Models 2 | { 3 | public class BasketItemExtendedModel 4 | { 5 | public int Quantity { get; set; } 6 | public string Color { get; set; } 7 | public decimal Price { get; set; } 8 | public string ProductId { get; set; } 9 | public string ProductName { get; set; } 10 | 11 | //Product Related Additional Fields 12 | public string Category { get; set; } 13 | public string Summary { get; set; } 14 | public string Description { get; set; } 15 | public string ImageFile { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Repositories/IProductRepository.cs: -------------------------------------------------------------------------------- 1 | using Catalog.API.Entities; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Catalog.API.Repositories 6 | { 7 | public interface IProductRepository 8 | { 9 | Task> GetProducts(); 10 | Task GetProduct(string id); 11 | Task> GetProductByName(string name); 12 | Task> GetProductByCategory(string categoryName); 13 | 14 | Task CreateProduct(Product product); 15 | Task UpdateProduct(Product product); 16 | Task DeleteProduct(string id); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ApiGateways/OcelotApiGw/OcelotApiGw.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\docker-compose.dcproj 6 | Linux 7 | ..\.. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Catalog.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\..\docker-compose.dcproj 6 | Linux 7 | ..\..\.. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Mappings/MappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Ordering.Application.Features.Orders.Commands.CheckoutOrder; 3 | using Ordering.Application.Features.Orders.Commands.UpdateOrder; 4 | using Ordering.Application.Features.Orders.Queries.GetOrdersList; 5 | using Ordering.Domain.Entities; 6 | 7 | namespace Ordering.Application.Mappings 8 | { 9 | public class MappingProfile : Profile 10 | { 11 | public MappingProfile() 12 | { 13 | CreateMap().ReverseMap(); 14 | CreateMap().ReverseMap(); 15 | CreateMap().ReverseMap(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Program.cs: -------------------------------------------------------------------------------- 1 | using Discount.API.Extensions; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Hosting; 4 | 5 | namespace Discount.API 6 | { 7 | public class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | var host = CreateHostBuilder(args).Build(); 12 | host.MigrateDatabase(); 13 | host.Run(); 14 | } 15 | 16 | public static IHostBuilder CreateHostBuilder(string[] args) => 17 | Host.CreateDefaultBuilder(args) 18 | .ConfigureWebHostDefaults(webBuilder => 19 | { 20 | webBuilder.UseStartup(); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Discount.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\..\docker-compose.dcproj 6 | Linux 7 | ..\..\.. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Entities/Product.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace Catalog.API.Entities 9 | { 10 | public class Product 11 | { 12 | [BsonId] 13 | [BsonRepresentation(BsonType.ObjectId)] 14 | public string Id { get; set; } 15 | 16 | [BsonElement("Name")] 17 | public string Name { get; set; } 18 | public string Category { get; set; } 19 | public string Summary { get; set; } 20 | public string Description { get; set; } 21 | public string ImageFile { get; set; } 22 | public decimal Price { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/docker-compose.dcproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.1 5 | Linux 6 | 321974d8-9cf5-4853-8bb7-d66247e54bf2 7 | LaunchBrowser 8 | {Scheme}://localhost:{ServicePort}/swagger 9 | catalog.api 10 | 11 | 12 | 13 | docker-compose.yml 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Services/BasketService.cs: -------------------------------------------------------------------------------- 1 | using Shopping.Aggregator.Extensions; 2 | using Shopping.Aggregator.Models; 3 | using System; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | 7 | namespace Shopping.Aggregator.Services 8 | { 9 | public class BasketService : IBasketService 10 | { 11 | private readonly HttpClient _client; 12 | 13 | public BasketService(HttpClient client) 14 | { 15 | _client = client ?? throw new ArgumentNullException(nameof(client)); 16 | } 17 | 18 | public async Task GetBasket(string userName) 19 | { 20 | var response = await _client.GetAsync($"/api/v1/Basket/{userName}"); 21 | return await response.ReadContentAs(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Extensions/HttpClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Text.Json; 4 | using System.Threading.Tasks; 5 | 6 | namespace Shopping.Aggregator.Extensions 7 | { 8 | public static class HttpClientExtensions 9 | { 10 | public static async Task ReadContentAs(this HttpResponseMessage response) 11 | { 12 | if (!response.IsSuccessStatusCode) 13 | throw new ApplicationException($"Something went wrong calling the API: {response.ReasonPhrase}"); 14 | 15 | var dataAsString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 16 | 17 | return JsonSerializer.Deserialize(dataAsString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ApiGateways/OcelotApiGw/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["ApiGateways/OcelotApiGw/OcelotApiGw.csproj", "ApiGateways/OcelotApiGw/"] 10 | RUN dotnet restore "ApiGateways/OcelotApiGw/OcelotApiGw.csproj" 11 | COPY . . 12 | WORKDIR "/src/ApiGateways/OcelotApiGw" 13 | RUN dotnet build "OcelotApiGw.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "OcelotApiGw.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "OcelotApiGw.dll"] 22 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace Basket.API 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace Catalog.API 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace Shopping.Aggregator 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["Services/Catalog/Catalog.API/Catalog.API.csproj", "Services/Catalog/Catalog.API/"] 10 | RUN dotnet restore "Services/Catalog/Catalog.API/Catalog.API.csproj" 11 | COPY . . 12 | WORKDIR "/src/Services/Catalog/Catalog.API" 13 | RUN dotnet build "Catalog.API.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "Catalog.API.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "Catalog.API.dll"] 22 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Discount - Backup.Grpc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\..\docker-compose.dcproj 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | EXPOSE 443 7 | 8 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 9 | WORKDIR /src 10 | COPY ["WebApps/AspnetRunBasics/AspnetRunBasics.csproj", "WebApps/AspnetRunBasics/"] 11 | RUN dotnet restore "WebApps/AspnetRunBasics/AspnetRunBasics.csproj" 12 | COPY . . 13 | WORKDIR "/src/WebApps/AspnetRunBasics" 14 | RUN dotnet build "AspnetRunBasics.csproj" -c Release -o /app/build 15 | 16 | FROM build AS publish 17 | RUN dotnet publish "AspnetRunBasics.csproj" -c Release -o /app/publish 18 | 19 | FROM base AS final 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "AspnetRunBasics.dll"] 23 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Services/OrderService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRunBasics.Extensions; 2 | using AspnetRunBasics.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | 8 | namespace AspnetRunBasics.Services 9 | { 10 | public class OrderService : IOrderService 11 | { 12 | private readonly HttpClient _client; 13 | 14 | public OrderService(HttpClient client) 15 | { 16 | _client = client ?? throw new ArgumentNullException(nameof(client)); 17 | } 18 | 19 | public async Task> GetOrdersByUserName(string userName) 20 | { 21 | var response = await _client.GetAsync($"/Order/{userName}"); 22 | return await response.ReadContentAs>(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["Services/Discount/Discount.API/Discount.API.csproj", "Services/Discount/Discount.API/"] 10 | RUN dotnet restore "Services/Discount/Discount.API/Discount.API.csproj" 11 | COPY . . 12 | WORKDIR "/src/Services/Discount/Discount.API" 13 | RUN dotnet build "Discount.API.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "Discount.API.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "Discount.API.dll"] 22 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["Services/Discount/Discount.Grpc/Discount.Grpc.csproj", "Services/Discount/Discount.Grpc/"] 10 | RUN dotnet restore "Services/Discount/Discount.Grpc/Discount.Grpc.csproj" 11 | COPY . . 12 | WORKDIR "/src/Services/Discount/Discount.Grpc" 13 | RUN dotnet build "Discount.Grpc.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "Discount.Grpc.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "Discount.Grpc.dll"] 22 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Entities/ShoppingCart.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Basket.API.Entities 4 | { 5 | public class ShoppingCart 6 | { 7 | public string UserName { get; set; } 8 | public List Items { get; set; } = new List(); 9 | 10 | public ShoppingCart() 11 | { 12 | } 13 | 14 | public ShoppingCart(string userName) 15 | { 16 | UserName = userName; 17 | } 18 | 19 | public decimal TotalPrice 20 | { 21 | get 22 | { 23 | decimal totalprice = 0; 24 | foreach (var item in Items) 25 | { 26 | totalprice += item.Price * item.Quantity; 27 | } 28 | return totalprice; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Entities/BasketCheckout.cs: -------------------------------------------------------------------------------- 1 | namespace Basket.API.Entities 2 | { 3 | public class BasketCheckout 4 | { 5 | public string UserName { get; set; } 6 | public decimal TotalPrice { get; set; } 7 | 8 | // BillingAddress 9 | public string FirstName { get; set; } 10 | public string LastName { get; set; } 11 | public string EmailAddress { get; set; } 12 | public string AddressLine { get; set; } 13 | public string Country { get; set; } 14 | public string State { get; set; } 15 | public string ZipCode { get; set; } 16 | 17 | // Payment 18 | public string CardName { get; set; } 19 | public string CardNumber { get; set; } 20 | public string Expiration { get; set; } 21 | public string CVV { get; set; } 22 | public int PaymentMethod { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["ApiGateways/Shopping.Aggregator/Shopping.Aggregator.csproj", "ApiGateways/Shopping.Aggregator/"] 10 | RUN dotnet restore "ApiGateways/Shopping.Aggregator/Shopping.Aggregator.csproj" 11 | COPY . . 12 | WORKDIR "/src/ApiGateways/Shopping.Aggregator" 13 | RUN dotnet build "Shopping.Aggregator.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "Shopping.Aggregator.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "Shopping.Aggregator.dll"] 22 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Models/BasketCheckoutModel.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRunBasics.Models 2 | { 3 | public class BasketCheckoutModel 4 | { 5 | public string UserName { get; set; } 6 | public decimal TotalPrice { get; set; } 7 | 8 | // BillingAddress 9 | public string FirstName { get; set; } 10 | public string LastName { get; set; } 11 | public string EmailAddress { get; set; } 12 | public string AddressLine { get; set; } 13 | public string Country { get; set; } 14 | public string State { get; set; } 15 | public string ZipCode { get; set; } 16 | 17 | // Payment 18 | public string CardName { get; set; } 19 | public string CardNumber { get; set; } 20 | public string Expiration { get; set; } 21 | public string CVV { get; set; } 22 | public int PaymentMethod { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Models/OrderResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRunBasics.Models 2 | { 3 | public class OrderResponseModel 4 | { 5 | public string UserName { get; set; } 6 | public decimal TotalPrice { get; set; } 7 | 8 | // BillingAddress 9 | public string FirstName { get; set; } 10 | public string LastName { get; set; } 11 | public string EmailAddress { get; set; } 12 | public string AddressLine { get; set; } 13 | public string Country { get; set; } 14 | public string State { get; set; } 15 | public string ZipCode { get; set; } 16 | 17 | // Payment 18 | public string CardName { get; set; } 19 | public string CardNumber { get; set; } 20 | public string Expiration { get; set; } 21 | public string CVV { get; set; } 22 | public int PaymentMethod { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Services/OrderService.cs: -------------------------------------------------------------------------------- 1 | using Shopping.Aggregator.Extensions; 2 | using Shopping.Aggregator.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace Shopping.Aggregator.Services 10 | { 11 | public class OrderService : IOrderService 12 | { 13 | private readonly HttpClient _client; 14 | 15 | public OrderService(HttpClient client) 16 | { 17 | _client = client ?? throw new ArgumentNullException(nameof(client)); 18 | } 19 | 20 | public async Task> GetOrdersByUserName(string userName) 21 | { 22 | var response = await _client.GetAsync($"/api/v1/Order/{userName}"); 23 | return await response.ReadContentAs>(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Models/OrderResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace Shopping.Aggregator.Models 2 | { 3 | public class OrderResponseModel 4 | { 5 | public string UserName { get; set; } 6 | public decimal TotalPrice { get; set; } 7 | 8 | // BillingAddress 9 | public string FirstName { get; set; } 10 | public string LastName { get; set; } 11 | public string EmailAddress { get; set; } 12 | public string AddressLine { get; set; } 13 | public string Country { get; set; } 14 | public string State { get; set; } 15 | public string ZipCode { get; set; } 16 | 17 | // Payment 18 | public string CardName { get; set; } 19 | public string CardNumber { get; set; } 20 | public string Expiration { get; set; } 21 | public string CVV { get; set; } 22 | public int PaymentMethod { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Ordering - Backup.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\..\docker-compose.dcproj 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Ordering.Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/BuildingBlocks/EventBus.Messages/Events/BasketCheckoutEvent.cs: -------------------------------------------------------------------------------- 1 | namespace EventBus.Messages.Events 2 | { 3 | public class BasketCheckoutEvent : IntegrationBaseEvent 4 | { 5 | public string UserName { get; set; } 6 | public decimal TotalPrice { get; set; } 7 | 8 | // BillingAddress 9 | public string FirstName { get; set; } 10 | public string LastName { get; set; } 11 | public string EmailAddress { get; set; } 12 | public string AddressLine { get; set; } 13 | public string Country { get; set; } 14 | public string State { get; set; } 15 | public string ZipCode { get; set; } 16 | 17 | // Payment 18 | public string CardName { get; set; } 19 | public string CardNumber { get; set; } 20 | public string Expiration { get; set; } 21 | public string CVV { get; set; } 22 | public int PaymentMethod { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["Services/Basket/Basket.API/Basket.API.csproj", "Services/Basket/Basket.API/"] 10 | COPY ["BuildingBlocks/EventBus.Messages/EventBus.Messages.csproj", "BuildingBlocks/EventBus.Messages/"] 11 | RUN dotnet restore "Services/Basket/Basket.API/Basket.API.csproj" 12 | COPY . . 13 | WORKDIR "/src/Services/Basket/Basket.API" 14 | RUN dotnet build "Basket.API.csproj" -c Release -o /app/build 15 | 16 | FROM build AS publish 17 | RUN dotnet publish "Basket.API.csproj" -c Release -o /app/publish 18 | 19 | FROM base AS final 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "Basket.API.dll"] -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Exceptions/ValidationException.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Results; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Ordering.Application.Exceptions 7 | { 8 | public class ValidationException : ApplicationException 9 | { 10 | public ValidationException() 11 | : base("One or more validation failures have occurred.") 12 | { 13 | Errors = new Dictionary(); 14 | } 15 | 16 | public ValidationException(IEnumerable failures) 17 | : this() 18 | { 19 | Errors = failures 20 | .GroupBy(e => e.PropertyName, e => e.ErrorMessage) 21 | .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); 22 | } 23 | 24 | public IDictionary Errors { get; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Data/CatalogContext.cs: -------------------------------------------------------------------------------- 1 | using Catalog.API.Entities; 2 | using Microsoft.Extensions.Configuration; 3 | using MongoDB.Driver; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace Catalog.API.Data 10 | { 11 | public class CatalogContext : ICatalogContext 12 | { 13 | public CatalogContext(IConfiguration configuration) 14 | { 15 | var client = new MongoClient(configuration.GetValue("DatabaseSettings:ConnectionString")); 16 | var database = client.GetDatabase(configuration.GetValue("DatabaseSettings:DatabaseName")); 17 | 18 | Products = database.GetCollection(configuration.GetValue("DatabaseSettings:CollectionName")); 19 | CatalogContextSeed.SeedData(Products); 20 | } 21 | 22 | public IMongoCollection Products { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Order.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using AspnetRunBasics.Models; 5 | using AspnetRunBasics.Services; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | 9 | namespace AspnetRunBasics 10 | { 11 | public class OrderModel : PageModel 12 | { 13 | private readonly IOrderService _orderService; 14 | 15 | public OrderModel(IOrderService orderService) 16 | { 17 | _orderService = orderService ?? throw new ArgumentNullException(nameof(orderService)); 18 | } 19 | 20 | public IEnumerable Orders { get; set; } = new List(); 21 | 22 | public async Task OnGetAsync() 23 | { 24 | Orders = await _orderService.GetOrdersByUserName("swn"); 25 | 26 | return Page(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/GrpcServices/DiscountGrpcService.cs: -------------------------------------------------------------------------------- 1 | using Discount.Grpc.Protos; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Basket.API.GrpcServices 8 | { 9 | public class DiscountGrpcService 10 | { 11 | private readonly DiscountProtoService.DiscountProtoServiceClient _discountProtoService; 12 | 13 | public DiscountGrpcService(DiscountProtoService.DiscountProtoServiceClient discountProtoService) 14 | { 15 | _discountProtoService = discountProtoService ?? throw new ArgumentNullException(nameof(discountProtoService)); 16 | } 17 | 18 | public async Task GetDiscount(string productName) 19 | { 20 | var discountRequest = new GetDiscountRequest { ProductName = productName }; 21 | return await _discountProtoService.GetDiscountAsync(discountRequest); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/ApplicationServiceRegistration.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using MediatR; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Ordering.Application.Behaviours; 5 | using System.Reflection; 6 | 7 | namespace Ordering.Application 8 | { 9 | public static class ApplicationServiceRegistration 10 | { 11 | public static IServiceCollection AddApplicationServices(this IServiceCollection services) 12 | { 13 | services.AddAutoMapper(Assembly.GetExecutingAssembly()); 14 | services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); 15 | services.AddMediatR(Assembly.GetExecutingAssembly()); 16 | 17 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>)); 18 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>)); 19 | 20 | return services; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Program.cs: -------------------------------------------------------------------------------- 1 | using Discount.Grpc.Extensions; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Hosting; 4 | 5 | namespace Discount.Grpc 6 | { 7 | public class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | var host = CreateHostBuilder(args).Build(); 12 | host.MigrateDatabase(); 13 | host.Run(); 14 | } 15 | 16 | // Additional configuration is required to successfully run gRPC on macOS. 17 | // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 18 | public static IHostBuilder CreateHostBuilder(string[] args) => 19 | Host.CreateDefaultBuilder(args) 20 | .ConfigureWebHostDefaults(webBuilder => 21 | { 22 | webBuilder.UseStartup(); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Commands/UpdateOrder/UpdateOrderCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace Ordering.Application.Features.Orders.Commands.UpdateOrder 4 | { 5 | public class UpdateOrderCommandValidator : AbstractValidator 6 | { 7 | public UpdateOrderCommandValidator() 8 | { 9 | RuleFor(p => p.UserName) 10 | .NotEmpty().WithMessage("{UserName} is required.") 11 | .NotNull() 12 | .MaximumLength(50).WithMessage("{UserName} must not exceed 50 characters."); 13 | 14 | RuleFor(p => p.EmailAddress) 15 | .NotEmpty().WithMessage("{EmailAddress} is required."); 16 | 17 | RuleFor(p => p.TotalPrice) 18 | .NotEmpty().WithMessage("{TotalPrice} is required.") 19 | .GreaterThan(0).WithMessage("{TotalPrice} should be greater than zero."); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Protos/discount.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option csharp_namespace = "Discount.Grpc.Protos"; 4 | 5 | service DiscountProtoService { 6 | rpc GetDiscount (GetDiscountRequest) returns (CouponModel); 7 | 8 | rpc CreateDiscount (CreateDiscountRequest) returns (CouponModel); 9 | rpc UpdateDiscount (UpdateDiscountRequest) returns (CouponModel); 10 | rpc DeleteDiscount (DeleteDiscountRequest) returns (DeleteDiscountResponse); 11 | } 12 | 13 | message GetDiscountRequest { 14 | string productName = 1; 15 | } 16 | 17 | message CouponModel { 18 | int32 id = 1; 19 | string productName = 2; 20 | string description = 3; 21 | int32 amount = 4; 22 | } 23 | 24 | message CreateDiscountRequest { 25 | CouponModel coupon = 1; 26 | } 27 | 28 | message UpdateDiscountRequest { 29 | CouponModel coupon = 1; 30 | } 31 | 32 | message DeleteDiscountRequest { 33 | string productName = 1; 34 | } 35 | 36 | message DeleteDiscountResponse { 37 | bool success = 1; 38 | } -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AspnetRunBasics.Pages 11 | { 12 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 13 | public class ErrorModel : PageModel 14 | { 15 | public string RequestId { get; set; } 16 | 17 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 18 | 19 | private readonly ILogger _logger; 20 | 21 | public ErrorModel(ILogger logger) 22 | { 23 | _logger = logger; 24 | } 25 | 26 | public void OnGet() 27 | { 28 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Ordering.Application.Contracts.Persistence; 3 | using Ordering.Domain.Entities; 4 | using Ordering.Infrastructure.Persistence; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Ordering.Infrastructure.Repositories 12 | { 13 | public class OrderRepository : RepositoryBase, IOrderRepository 14 | { 15 | public OrderRepository(OrderContext dbContext) : base(dbContext) 16 | { 17 | } 18 | 19 | public async Task> GetOrdersByUserName(string userName) 20 | { 21 | var orderList = await _dbContext.Orders 22 | .Where(o => o.UserName == userName) 23 | .ToListAsync(); 24 | return orderList; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Commands/CheckoutOrder/CheckoutOrderCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace Ordering.Application.Features.Orders.Commands.CheckoutOrder 4 | { 5 | public class CheckoutOrderCommandValidator : AbstractValidator 6 | { 7 | public CheckoutOrderCommandValidator() 8 | { 9 | RuleFor(p => p.UserName) 10 | .NotEmpty().WithMessage("{UserName} is required.") 11 | .NotNull() 12 | .MaximumLength(50).WithMessage("{UserName} must not exceed 50 characters."); 13 | 14 | RuleFor(p => p.EmailAddress) 15 | .NotEmpty().WithMessage("{EmailAddress} is required."); 16 | 17 | RuleFor(p => p.TotalPrice) 18 | .NotEmpty().WithMessage("{TotalPrice} is required.") 19 | .GreaterThan(0).WithMessage("{TotalPrice} should be greater than zero."); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ApiGateways/OcelotApiGw/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:4938", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Local" 16 | } 17 | }, 18 | "OcelotApiGw": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "dotnetRunMessages": "true", 25 | "applicationUrl": "http://localhost:5010" 26 | }, 27 | "Docker": { 28 | "commandName": "Docker", 29 | "launchBrowser": true, 30 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 31 | "publishAllPorts": true 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Queries/GetOrdersList/OrdersVm.cs: -------------------------------------------------------------------------------- 1 | namespace Ordering.Application.Features.Orders.Queries.GetOrdersList 2 | { 3 | public class OrdersVm 4 | { 5 | public int Id { get; set; } 6 | public string UserName { get; set; } 7 | public decimal TotalPrice { get; set; } 8 | 9 | // BillingAddress 10 | public string FirstName { get; set; } 11 | public string LastName { get; set; } 12 | public string EmailAddress { get; set; } 13 | public string AddressLine { get; set; } 14 | public string Country { get; set; } 15 | public string State { get; set; } 16 | public string ZipCode { get; set; } 17 | 18 | // Payment 19 | public string CardName { get; set; } 20 | public string CardNumber { get; set; } 21 | public string Expiration { get; set; } 22 | public string CVV { get; set; } 23 | public int PaymentMethod { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:51063/", 7 | "sslPort": 44334 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "AspnetRunBasics": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:5006" 25 | }, 26 | "Docker": { 27 | "commandName": "Docker", 28 | "launchBrowser": true, 29 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 30 | "publishAllPorts": true, 31 | "useSSL": true 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Commands/CheckoutOrder/CheckoutOrderCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace Ordering.Application.Features.Orders.Commands.CheckoutOrder 4 | { 5 | public class CheckoutOrderCommand : IRequest 6 | { 7 | public string UserName { get; set; } 8 | public decimal TotalPrice { get; set; } 9 | 10 | // BillingAddress 11 | public string FirstName { get; set; } 12 | public string LastName { get; set; } 13 | public string EmailAddress { get; set; } 14 | public string AddressLine { get; set; } 15 | public string Country { get; set; } 16 | public string State { get; set; } 17 | public string ZipCode { get; set; } 18 | 19 | // Payment 20 | public string CardName { get; set; } 21 | public string CardNumber { get; set; } 22 | public string Expiration { get; set; } 23 | public string CVV { get; set; } 24 | public int PaymentMethod { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Commands/UpdateOrder/UpdateOrderCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace Ordering.Application.Features.Orders.Commands.UpdateOrder 4 | { 5 | public class UpdateOrderCommand : IRequest 6 | { 7 | public int Id { get; set; } 8 | public string UserName { get; set; } 9 | public decimal TotalPrice { get; set; } 10 | 11 | // BillingAddress 12 | public string FirstName { get; set; } 13 | public string LastName { get; set; } 14 | public string EmailAddress { get; set; } 15 | public string AddressLine { get; set; } 16 | public string Country { get; set; } 17 | public string State { get; set; } 18 | public string ZipCode { get; set; } 19 | 20 | // Payment 21 | public string CardName { get; set; } 22 | public string CardNumber { get; set; } 23 | public string Expiration { get; set; } 24 | public string CVV { get; set; } 25 | public int PaymentMethod { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Discount.Grpc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\..\docker-compose.dcproj 6 | Linux 7 | ..\..\.. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Domain/Entities/Order.cs: -------------------------------------------------------------------------------- 1 | using Ordering.Domain.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ordering.Domain.Entities 9 | { 10 | public class Order : EntityBase 11 | { 12 | public string UserName { get; set; } 13 | public decimal TotalPrice { get; set; } 14 | 15 | // BillingAddress 16 | public string FirstName { get; set; } 17 | public string LastName { get; set; } 18 | public string EmailAddress { get; set; } 19 | public string AddressLine { get; set; } 20 | public string Country { get; set; } 21 | public string State { get; set; } 22 | public string ZipCode { get; set; } 23 | 24 | // Payment 25 | public string CardName { get; set; } 26 | public string CardNumber { get; set; } 27 | public string Expiration { get; set; } 28 | public string CVV { get; set; } 29 | public int PaymentMethod { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mehmet Özkaya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Shared/_TopProductPartial.cshtml: -------------------------------------------------------------------------------- 1 | @model CatalogModel 2 | 3 |
4 |
5 |
6 | Top product 7 |
8 | Card image cap 9 |
10 |

@Model.Name

11 |
12 |
13 |

@Model.Price $

14 |
15 |
16 | View 17 |
18 |
19 |
20 |
21 |
-------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:49945", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "Project", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Basket.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5001" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:23272", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Catalog.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5000" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:11915", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Discount.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5002" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:56786", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Ordering.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5004" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:14764", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Shopping.Aggregator": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5005" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Contracts/Persistence/IAsyncRepository.cs: -------------------------------------------------------------------------------- 1 | using Ordering.Domain.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ordering.Application.Contracts.Persistence 9 | { 10 | public interface IAsyncRepository where T : EntityBase 11 | { 12 | Task> GetAllAsync(); 13 | Task> GetAsync(Expression> predicate); 14 | Task> GetAsync(Expression> predicate = null, 15 | Func, IOrderedQueryable> orderBy = null, 16 | string includeString = null, 17 | bool disableTracking = true); 18 | Task> GetAsync(Expression> predicate = null, 19 | Func, IOrderedQueryable> orderBy = null, 20 | List>> includes = null, 21 | bool disableTracking = true); 22 | Task GetByIdAsync(int id); 23 | Task AddAsync(T entity); 24 | Task UpdateAsync(T entity); 25 | Task DeleteAsync(T entity); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | using Microsoft.Extensions.Logging; 4 | using Ordering.API.Extensions; 5 | using Ordering.Infrastructure.Persistence; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace Ordering.API 9 | { 10 | public class Program 11 | { 12 | public static void Main(string[] args) 13 | { 14 | CreateHostBuilder(args) 15 | .Build() 16 | .MigrateDatabase((context, services) => 17 | { 18 | var logger = services.GetService>(); 19 | OrderContextSeed 20 | .SeedAsync(context, logger) 21 | .Wait(); 22 | }) 23 | .Run(); 24 | } 25 | 26 | public static IHostBuilder CreateHostBuilder(string[] args) => 27 | Host.CreateDefaultBuilder(args) 28 | .ConfigureWebHostDefaults(webBuilder => 29 | { 30 | webBuilder.UseStartup(); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Infrastructure/Persistence/OrderContextSeed.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Ordering.Domain.Entities; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ordering.Infrastructure.Persistence 8 | { 9 | public class OrderContextSeed 10 | { 11 | public static async Task SeedAsync(OrderContext orderContext, ILogger logger) 12 | { 13 | if (!orderContext.Orders.Any()) 14 | { 15 | orderContext.Orders.AddRange(GetPreconfiguredOrders()); 16 | await orderContext.SaveChangesAsync(); 17 | logger.LogInformation("Seed database associated with context {DbContextName}", typeof(OrderContext).Name); 18 | } 19 | } 20 | 21 | private static IEnumerable GetPreconfiguredOrders() 22 | { 23 | return new List 24 | { 25 | new Order() {UserName = "swn", FirstName = "Mehmet", LastName = "Ozkaya", EmailAddress = "ezozkme@gmail.com", AddressLine = "Bahcelievler", Country = "Turkey", TotalPrice = 350 } 26 | }; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Behaviours/UnhandledExceptionBehaviour.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ordering.Application.Behaviours 8 | { 9 | public class UnhandledExceptionBehaviour : IPipelineBehavior 10 | { 11 | private readonly ILogger _logger; 12 | 13 | public UnhandledExceptionBehaviour(ILogger logger) 14 | { 15 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 16 | } 17 | 18 | public async Task Handle(TRequest request, CancellationToken cancellationToken, 19 | RequestHandlerDelegate next) 20 | { 21 | try 22 | { 23 | return await next(); 24 | } 25 | catch (Exception ex) 26 | { 27 | var requestName = typeof(TRequest).Name; 28 | _logger.LogError(ex, "Application Request: Unhandled Exception for Request {Name} {@Request}", requestName, request); 29 | throw; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Shared/_ProductItemPartial.cshtml: -------------------------------------------------------------------------------- 1 | @model CatalogModel 2 | 3 |
4 | 5 |
6 |

@Model.Name

7 |

@Model.Summary

8 |
9 |
10 |

@Model.Price $

11 |
12 |
13 | 14 | @*Add to Cart*@ 15 | 16 |
17 | Add to Cart 18 | 19 |
20 | 21 |
22 |
23 |
24 |
-------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Queries/GetOrdersList/GetOrdersListQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediatR; 3 | using Ordering.Application.Contracts.Persistence; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace Ordering.Application.Features.Orders.Queries.GetOrdersList 12 | { 13 | public class GetOrdersListQueryHandler : IRequestHandler> 14 | { 15 | private readonly IOrderRepository _orderRepository; 16 | private readonly IMapper _mapper; 17 | 18 | public GetOrdersListQueryHandler(IOrderRepository orderRepository, IMapper mapper) 19 | { 20 | _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); 21 | _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); 22 | } 23 | 24 | public async Task> Handle(GetOrdersListQuery request, 25 | CancellationToken cancellationToken) 26 | { 27 | var orderList = await _orderRepository.GetOrdersByUserName(request.UserName); 28 | return _mapper.Map>(orderList); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 8 | WORKDIR /src 9 | COPY ["Services/Ordering/Ordering.API/Ordering.API.csproj", "Services/Ordering/Ordering.API/"] 10 | COPY ["Services/Ordering/Ordering.Application/Ordering.Application.csproj", "Services/Ordering/Ordering.Application/"] 11 | COPY ["Services/Ordering/Ordering.Domain/Ordering.Domain.csproj", "Services/Ordering/Ordering.Domain/"] 12 | COPY ["BuildingBlocks/EventBus.Messages/EventBus.Messages.csproj", "BuildingBlocks/EventBus.Messages/"] 13 | COPY ["Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj", "Services/Ordering/Ordering.Infrastructure/"] 14 | RUN dotnet restore "Services/Ordering/Ordering.API/Ordering.API.csproj" 15 | COPY . . 16 | WORKDIR "/src/Services/Ordering/Ordering.API" 17 | RUN dotnet build "Ordering.API.csproj" -c Release -o /app/build 18 | 19 | FROM build AS publish 20 | RUN dotnet publish "Ordering.API.csproj" -c Release -o /app/publish 21 | 22 | FROM base AS final 23 | WORKDIR /app 24 | COPY --from=publish /app/publish . 25 | ENTRYPOINT ["dotnet", "Ordering.API.dll"] -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Infrastructure/InfrastructureServiceRegistration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Ordering.Application.Contracts.Infrastructure; 5 | using Ordering.Application.Contracts.Persistence; 6 | using Ordering.Application.Models; 7 | using Ordering.Infrastructure.Mail; 8 | using Ordering.Infrastructure.Persistence; 9 | using Ordering.Infrastructure.Repositories; 10 | 11 | namespace Ordering.Infrastructure 12 | { 13 | public static class InfrastructureServiceRegistration 14 | { 15 | public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration) 16 | { 17 | services.AddDbContext(options => 18 | options.UseSqlServer(configuration.GetConnectionString("OrderingConnectionString"))); 19 | 20 | services.AddScoped(typeof(IAsyncRepository<>), typeof(RepositoryBase<>)); 21 | services.AddScoped(); 22 | 23 | services.Configure(c => configuration.GetSection("EmailSettings")); 24 | services.AddTransient(); 25 | 26 | return services; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Repositories/BasketRepository.cs: -------------------------------------------------------------------------------- 1 | using Basket.API.Entities; 2 | using Microsoft.Extensions.Caching.Distributed; 3 | using Newtonsoft.Json; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace Basket.API.Repositories 8 | { 9 | public class BasketRepository : IBasketRepository 10 | { 11 | private readonly IDistributedCache _redisCache; 12 | 13 | public BasketRepository(IDistributedCache redisCache) 14 | { 15 | _redisCache = redisCache ?? throw new ArgumentNullException(nameof(redisCache)); 16 | } 17 | 18 | public async Task GetBasket(string userName) 19 | { 20 | var basket = await _redisCache.GetStringAsync(userName); 21 | 22 | if (String.IsNullOrEmpty(basket)) 23 | return null; 24 | 25 | return JsonConvert.DeserializeObject(basket); 26 | } 27 | 28 | public async Task UpdateBasket(ShoppingCart basket) 29 | { 30 | await _redisCache.SetStringAsync(basket.UserName, JsonConvert.SerializeObject(basket)); 31 | 32 | return await GetBasket(basket.UserName); 33 | } 34 | 35 | public async Task DeleteBasket(string userName) 36 | { 37 | await _redisCache.RemoveAsync(userName); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ApiGateways/OcelotApiGw/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace OcelotApiGw 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureAppConfiguration((hostingContext, config) => 22 | { 23 | config.AddJsonFile($"ocelot.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true); 24 | }) 25 | .ConfigureWebHostDefaults(webBuilder => 26 | { 27 | webBuilder.UseStartup(); 28 | }) 29 | .ConfigureLogging((hostingContext, loggingbuilder) => 30 | { 31 | loggingbuilder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); 32 | loggingbuilder.AddConsole(); 33 | loggingbuilder.AddDebug(); 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Infrastructure/Persistence/OrderContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Ordering.Domain.Common; 3 | using Ordering.Domain.Entities; 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ordering.Infrastructure.Persistence 9 | { 10 | public class OrderContext : DbContext 11 | { 12 | public OrderContext(DbContextOptions options) : base(options) 13 | { 14 | } 15 | 16 | public DbSet Orders { get; set; } 17 | 18 | public override Task SaveChangesAsync(CancellationToken cancellationToken = default) 19 | { 20 | foreach (var entry in ChangeTracker.Entries()) 21 | { 22 | switch (entry.State) 23 | { 24 | case EntityState.Added: 25 | entry.Entity.CreatedDate = DateTime.Now; 26 | entry.Entity.CreatedBy = "swn"; 27 | break; 28 | case EntityState.Modified: 29 | entry.Entity.LastModifiedDate = DateTime.Now; 30 | entry.Entity.LastModifiedBy = "swn"; 31 | break; 32 | } 33 | } 34 | 35 | return base.SaveChangesAsync(cancellationToken); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Services/CatalogService.cs: -------------------------------------------------------------------------------- 1 | using Shopping.Aggregator.Extensions; 2 | using Shopping.Aggregator.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | 8 | namespace Shopping.Aggregator.Services 9 | { 10 | public class CatalogService : ICatalogService 11 | { 12 | private readonly HttpClient _client; 13 | 14 | public CatalogService(HttpClient client) 15 | { 16 | _client = client ?? throw new ArgumentNullException(nameof(client)); 17 | } 18 | 19 | public async Task> GetCatalog() 20 | { 21 | var response = await _client.GetAsync("/api/v1/Catalog"); 22 | return await response.ReadContentAs>(); 23 | } 24 | 25 | public async Task GetCatalog(string id) 26 | { 27 | var response = await _client.GetAsync($"/api/v1/Catalog/{id}"); 28 | return await response.ReadContentAs(); 29 | } 30 | 31 | public async Task> GetCatalogByCategory(string category) 32 | { 33 | var response = await _client.GetAsync($"/api/v1/Catalog/GetProductByCategory/{category}"); 34 | return await response.ReadContentAs>(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Cart.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using AspnetRunBasics.Models; 5 | using AspnetRunBasics.Services; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | 9 | namespace AspnetRunBasics 10 | { 11 | public class CartModel : PageModel 12 | { 13 | private readonly IBasketService _basketService; 14 | 15 | public CartModel(IBasketService basketService) 16 | { 17 | _basketService = basketService ?? throw new ArgumentNullException(nameof(basketService)); 18 | } 19 | 20 | public BasketModel Cart { get; set; } = new BasketModel(); 21 | 22 | public async Task OnGetAsync() 23 | { 24 | var userName = "swn"; 25 | Cart = await _basketService.GetBasket(userName); 26 | 27 | return Page(); 28 | } 29 | 30 | public async Task OnPostRemoveToCartAsync(string productId) 31 | { 32 | var userName = "swn"; 33 | var basket = await _basketService.GetBasket(userName); 34 | 35 | var item = basket.Items.Single(x => x.ProductId == productId); 36 | basket.Items.Remove(item); 37 | 38 | var basketUpdated = await _basketService.UpdateBasket(basket); 39 | 40 | return RedirectToPage(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/EventBusConsumer/BasketCheckoutConsumer.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using EventBus.Messages.Events; 3 | using MassTransit; 4 | using MediatR; 5 | using Microsoft.Extensions.Logging; 6 | using Ordering.Application.Features.Orders.Commands.CheckoutOrder; 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ordering.API.EventBusConsumer 11 | { 12 | public class BasketCheckoutConsumer : IConsumer 13 | { 14 | private readonly IMediator _mediator; 15 | private readonly IMapper _mapper; 16 | private readonly ILogger _logger; 17 | 18 | public BasketCheckoutConsumer(IMediator mediator, IMapper mapper, ILogger logger) 19 | { 20 | _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); 21 | _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); 22 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 23 | } 24 | 25 | public async Task Consume(ConsumeContext context) 26 | { 27 | var command = _mapper.Map(context.Message); 28 | var result = await _mediator.Send(command); 29 | 30 | _logger.LogInformation("BasketCheckoutEvent consumed successfully. Created Order Id : {newOrderId}", result); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/wwwroot/css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | ** Style Simple Ecommerce Theme for Bootstrap 4 3 | ** Created by T-PHP https://t-php.fr/43-theme-ecommerce-bootstrap-4.html 4 | */ 5 | .bloc_left_price { 6 | color: #c01508; 7 | text-align: center; 8 | font-weight: bold; 9 | font-size: 150%; 10 | } 11 | .category_block li:hover { 12 | background-color: #007bff; 13 | } 14 | .category_block li:hover a { 15 | color: #ffffff; 16 | } 17 | .category_block li a { 18 | color: #343a40; 19 | } 20 | .add_to_cart_block .price { 21 | color: #c01508; 22 | text-align: center; 23 | font-weight: bold; 24 | font-size: 200%; 25 | margin-bottom: 0; 26 | } 27 | .add_to_cart_block .price_discounted { 28 | color: #343a40; 29 | text-align: center; 30 | text-decoration: line-through; 31 | font-size: 140%; 32 | } 33 | .product_rassurance { 34 | padding: 10px; 35 | margin-top: 15px; 36 | background: #ffffff; 37 | border: 1px solid #6c757d; 38 | color: #6c757d; 39 | } 40 | .product_rassurance .list-inline { 41 | margin-bottom: 0; 42 | text-transform: uppercase; 43 | text-align: center; 44 | } 45 | .product_rassurance .list-inline li:hover { 46 | color: #343a40; 47 | } 48 | .reviews_product .fa-star { 49 | color: gold; 50 | } 51 | .pagination { 52 | margin-top: 20px; 53 | } 54 | footer { 55 | background: #343a40; 56 | padding: 40px; 57 | } 58 | footer a { 59 | color: #f8f9fa!important 60 | } 61 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Domain/Common/ValueObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Ordering.Domain.Common 6 | { 7 | // Learn more: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/implement-value-objects 8 | public abstract class ValueObject 9 | { 10 | protected static bool EqualOperator(ValueObject left, ValueObject right) 11 | { 12 | if (left is null ^ right is null) 13 | { 14 | return false; 15 | } 16 | 17 | return left?.Equals(right) != false; 18 | } 19 | 20 | protected static bool NotEqualOperator(ValueObject left, ValueObject right) 21 | { 22 | return !(EqualOperator(left, right)); 23 | } 24 | 25 | protected abstract IEnumerable GetEqualityComponents(); 26 | 27 | public override bool Equals(object obj) 28 | { 29 | if (obj == null || obj.GetType() != GetType()) 30 | { 31 | return false; 32 | } 33 | 34 | var other = (ValueObject)obj; 35 | return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); 36 | } 37 | 38 | public override int GetHashCode() 39 | { 40 | return GetEqualityComponents() 41 | .Select(x => x != null ? x.GetHashCode() : 0) 42 | .Aggregate((x, y) => x ^ y); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Basket.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\..\docker-compose.dcproj 6 | Linux 7 | ..\..\.. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Protos\discount.proto 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Behaviours/ValidationBehaviour.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using MediatR; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using ValidationException = Ordering.Application.Exceptions.ValidationException; 9 | 10 | namespace Ordering.Application.Behaviours 11 | { 12 | public class ValidationBehaviour : IPipelineBehavior 13 | { 14 | private readonly IEnumerable> _validators; 15 | 16 | public ValidationBehaviour(IEnumerable> validators) 17 | { 18 | _validators = validators ?? throw new ArgumentNullException(nameof(validators)); 19 | } 20 | 21 | public async Task Handle(TRequest request, CancellationToken cancellationToken, 22 | RequestHandlerDelegate next) 23 | { 24 | if (_validators.Any()) 25 | { 26 | var context = new ValidationContext(request); 27 | 28 | var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken))); 29 | var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList(); 30 | 31 | if (failures.Count != 0) 32 | throw new ValidationException(failures); 33 | } 34 | 35 | return await next(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Services/BasketService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRunBasics.Extensions; 2 | using AspnetRunBasics.Models; 3 | using System; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | 7 | namespace AspnetRunBasics.Services 8 | { 9 | public class BasketService : IBasketService 10 | { 11 | private readonly HttpClient _client; 12 | 13 | public BasketService(HttpClient client) 14 | { 15 | _client = client ?? throw new ArgumentNullException(nameof(client)); 16 | } 17 | 18 | public async Task GetBasket(string userName) 19 | { 20 | var response = await _client.GetAsync($"/Basket/{userName}"); 21 | return await response.ReadContentAs(); 22 | } 23 | 24 | public async Task UpdateBasket(BasketModel model) 25 | { 26 | var response = await _client.PostAsJson($"/Basket", model); 27 | if (response.IsSuccessStatusCode) 28 | return await response.ReadContentAs(); 29 | else 30 | { 31 | throw new Exception("Something went wrong when calling api."); 32 | } 33 | } 34 | 35 | public async Task CheckoutBasket(BasketCheckoutModel model) 36 | { 37 | var response = await _client.PostAsJson($"/Basket/Checkout", model); 38 | if (!response.IsSuccessStatusCode) 39 | { 40 | throw new Exception("Something went wrong when calling api."); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Ordering.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | ..\..\..\docker-compose.dcproj 6 | Linux 7 | ..\..\.. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/ApiGateways/OcelotApiGw/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Ocelot.DependencyInjection; 7 | using Ocelot.Middleware; 8 | using Ocelot.Cache.CacheManager; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Threading.Tasks; 13 | 14 | namespace OcelotApiGw 15 | { 16 | public class Startup 17 | { 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | services.AddOcelot() 23 | .AddCacheManager(settings => settings.WithDictionaryHandle()); 24 | } 25 | 26 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 27 | public async void Configure(IApplicationBuilder app, IWebHostEnvironment env) 28 | { 29 | if (env.IsDevelopment()) 30 | { 31 | app.UseDeveloperExceptionPage(); 32 | } 33 | 34 | app.UseRouting(); 35 | 36 | app.UseEndpoints(endpoints => 37 | { 38 | endpoints.MapGet("/", async context => 39 | { 40 | await context.Response.WriteAsync("Hello World!"); 41 | }); 42 | }); 43 | 44 | await app.UseOcelot(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Extensions/HttpClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Net.Http.Headers; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | 7 | namespace AspnetRunBasics.Extensions 8 | { 9 | public static class HttpClientExtensions 10 | { 11 | public static async Task ReadContentAs(this HttpResponseMessage response) 12 | { 13 | if (!response.IsSuccessStatusCode) 14 | throw new ApplicationException($"Something went wrong calling the API: {response.ReasonPhrase}"); 15 | 16 | var dataAsString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 17 | 18 | return JsonSerializer.Deserialize(dataAsString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); 19 | } 20 | 21 | public static Task PostAsJson(this HttpClient httpClient, string url, T data) 22 | { 23 | var dataAsString = JsonSerializer.Serialize(data); 24 | var content = new StringContent(dataAsString); 25 | content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 26 | 27 | return httpClient.PostAsync(url, content); 28 | } 29 | 30 | public static Task PutAsJson(this HttpClient httpClient, string url, T data) 31 | { 32 | var dataAsString = JsonSerializer.Serialize(data); 33 | var content = new StringContent(dataAsString); 34 | content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 35 | 36 | return httpClient.PutAsync(url, content); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/CheckOut.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using AspnetRunBasics.Models; 4 | using AspnetRunBasics.Services; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace AspnetRunBasics 9 | { 10 | public class CheckOutModel : PageModel 11 | { 12 | private readonly IBasketService _basketService; 13 | private readonly IOrderService _orderService; 14 | 15 | public CheckOutModel(IBasketService basketService, IOrderService orderService) 16 | { 17 | _basketService = basketService ?? throw new ArgumentNullException(nameof(basketService)); 18 | _orderService = orderService ?? throw new ArgumentNullException(nameof(orderService)); 19 | } 20 | 21 | [BindProperty] 22 | public BasketCheckoutModel Order { get; set; } 23 | 24 | public BasketModel Cart { get; set; } = new BasketModel(); 25 | 26 | public async Task OnGetAsync() 27 | { 28 | var userName = "swn"; 29 | Cart = await _basketService.GetBasket(userName); 30 | 31 | return Page(); 32 | } 33 | 34 | public async Task OnPostCheckOutAsync() 35 | { 36 | var userName = "swn"; 37 | Cart = await _basketService.GetBasket(userName); 38 | 39 | if (!ModelState.IsValid) 40 | { 41 | return Page(); 42 | } 43 | 44 | Order.UserName = userName; 45 | Order.TotalPrice = Cart.TotalPrice; 46 | 47 | await _basketService.CheckoutBasket(Order); 48 | 49 | return RedirectToPage("Confirmation", "OrderSubmitted"); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Infrastructure/Mail/EmailService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Options; 3 | using Ordering.Application.Contracts.Infrastructure; 4 | using Ordering.Application.Models; 5 | using SendGrid; 6 | using SendGrid.Helpers.Mail; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ordering.Infrastructure.Mail 10 | { 11 | public class EmailService : IEmailService 12 | { 13 | public EmailSettings _emailSettings { get; } 14 | public ILogger _logger { get; } 15 | 16 | public EmailService(IOptions emailSettings, ILogger logger) 17 | { 18 | _emailSettings = emailSettings.Value; 19 | _logger = logger; 20 | } 21 | 22 | public async Task SendEmail(Email email) 23 | { 24 | var client = new SendGridClient(_emailSettings.ApiKey); 25 | 26 | var subject = email.Subject; 27 | var to = new EmailAddress(email.To); 28 | var emailBody = email.Body; 29 | 30 | var from = new EmailAddress 31 | { 32 | Email = _emailSettings.FromAddress, 33 | Name = _emailSettings.FromName 34 | }; 35 | 36 | var sendGridMessage = MailHelper.CreateSingleEmail(from, to, subject, emailBody, emailBody); 37 | var response = await client.SendEmailAsync(sendGridMessage); 38 | 39 | _logger.LogInformation("Email sent."); 40 | 41 | if (response.StatusCode == System.Net.HttpStatusCode.Accepted || response.StatusCode == System.Net.HttpStatusCode.OK) 42 | return true; 43 | 44 | _logger.LogError("Email sending failed."); 45 | return false; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Services/CatalogService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRunBasics.Extensions; 2 | using AspnetRunBasics.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | 8 | namespace AspnetRunBasics.Services 9 | { 10 | public class CatalogService : ICatalogService 11 | { 12 | private readonly HttpClient _client; 13 | 14 | public CatalogService(HttpClient client) 15 | { 16 | _client = client ?? throw new ArgumentNullException(nameof(client)); 17 | } 18 | 19 | public async Task> GetCatalog() 20 | { 21 | var response = await _client.GetAsync("/Catalog"); 22 | return await response.ReadContentAs>(); 23 | } 24 | 25 | public async Task GetCatalog(string id) 26 | { 27 | var response = await _client.GetAsync($"/Catalog/{id}"); 28 | return await response.ReadContentAs(); 29 | } 30 | 31 | public async Task> GetCatalogByCategory(string category) 32 | { 33 | var response = await _client.GetAsync($"/Catalog/GetProductByCategory/{category}"); 34 | return await response.ReadContentAs>(); 35 | } 36 | 37 | public async Task CreateCatalog(CatalogModel model) 38 | { 39 | var response = await _client.PostAsJson($"/Catalog", model); 40 | if (response.IsSuccessStatusCode) 41 | return await response.ReadContentAs(); 42 | else 43 | { 44 | throw new Exception("Something went wrong when calling api."); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using AspnetRunBasics.Models; 5 | using AspnetRunBasics.Services; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | 9 | namespace AspnetRunBasics.Pages 10 | { 11 | public class IndexModel : PageModel 12 | { 13 | private readonly ICatalogService _catalogService; 14 | private readonly IBasketService _basketService; 15 | 16 | public IndexModel(ICatalogService catalogService, IBasketService basketService) 17 | { 18 | _catalogService = catalogService ?? throw new ArgumentNullException(nameof(catalogService)); 19 | _basketService = basketService ?? throw new ArgumentNullException(nameof(basketService)); 20 | } 21 | 22 | public IEnumerable ProductList { get; set; } = new List(); 23 | 24 | public async Task OnGetAsync() 25 | { 26 | ProductList = await _catalogService.GetCatalog(); 27 | return Page(); 28 | } 29 | 30 | public async Task OnPostAddToCartAsync(string productId) 31 | { 32 | var product = await _catalogService.GetCatalog(productId); 33 | 34 | var userName = "swn"; 35 | var basket = await _basketService.GetBasket(userName); 36 | 37 | basket.Items.Add(new BasketItemModel 38 | { 39 | ProductId = productId, 40 | ProductName = product.Name, 41 | Price = product.Price, 42 | Quantity = 1, 43 | Color = "Black" 44 | }); 45 | 46 | var basketUpdated = await _basketService.UpdateBasket(basket); 47 | return RedirectToPage("Cart"); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Commands/DeleteOrder/DeleteOrderCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediatR; 3 | using Microsoft.Extensions.Logging; 4 | using Ordering.Application.Contracts.Persistence; 5 | using Ordering.Application.Exceptions; 6 | using Ordering.Domain.Entities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | 14 | namespace Ordering.Application.Features.Orders.Commands.DeleteOrder 15 | { 16 | public class DeleteOrderCommandHandler : IRequestHandler 17 | { 18 | private readonly IOrderRepository _orderRepository; 19 | private readonly IMapper _mapper; 20 | private readonly ILogger _logger; 21 | 22 | public DeleteOrderCommandHandler(IOrderRepository orderRepository, IMapper mapper, ILogger logger) 23 | { 24 | _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); 25 | _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); 26 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 27 | } 28 | 29 | public async Task Handle(DeleteOrderCommand request, CancellationToken cancellationToken) 30 | { 31 | var orderToDelete = await _orderRepository.GetByIdAsync(request.Id); 32 | if (orderToDelete == null) 33 | { 34 | throw new NotFoundException(nameof(Order), request.Id); 35 | } 36 | 37 | await _orderRepository.DeleteAsync(orderToDelete); 38 | _logger.LogInformation($"Order {orderToDelete.Id} is successfully deleted."); 39 | 40 | return Unit.Value; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Startup.cs: -------------------------------------------------------------------------------- 1 | using Discount.Grpc.Repositories; 2 | using Discount.Grpc.Services; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Threading.Tasks; 12 | 13 | namespace Discount.Grpc 14 | { 15 | public class Startup 16 | { 17 | // This method gets called by the runtime. Use this method to add services to the container. 18 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddScoped(); 22 | services.AddAutoMapper(typeof(Startup)); 23 | services.AddGrpc(); 24 | } 25 | 26 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 27 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 28 | { 29 | if (env.IsDevelopment()) 30 | { 31 | app.UseDeveloperExceptionPage(); 32 | } 33 | 34 | app.UseRouting(); 35 | 36 | app.UseEndpoints(endpoints => 37 | { 38 | endpoints.MapGrpcService(); 39 | 40 | endpoints.MapGet("/", async context => 41 | { 42 | await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); 43 | }); 44 | }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | catalogdb: 5 | image: mongo 6 | 7 | basketdb: 8 | image: redis:alpine 9 | 10 | discountdb: 11 | image: postgres 12 | 13 | orderdb: 14 | image: mcr.microsoft.com/mssql/server:2017-latest 15 | 16 | rabbitmq: 17 | image: rabbitmq:3-management-alpine 18 | 19 | pgadmin: 20 | image: dpage/pgadmin4 21 | 22 | portainer: 23 | image: portainer/portainer-ce 24 | 25 | catalog.api: 26 | image: ${DOCKER_REGISTRY-}catalogapi 27 | build: 28 | context: . 29 | dockerfile: Services/Catalog/Catalog.API/Dockerfile 30 | 31 | basket.api: 32 | image: ${DOCKER_REGISTRY-}basketapi 33 | build: 34 | context: . 35 | dockerfile: Services/Basket/Basket.API/Dockerfile 36 | 37 | discount.api: 38 | image: ${DOCKER_REGISTRY-}discountapi 39 | build: 40 | context: . 41 | dockerfile: Services/Discount/Discount.API/Dockerfile 42 | 43 | discount.grpc: 44 | image: ${DOCKER_REGISTRY-}discountgrpc 45 | build: 46 | context: . 47 | dockerfile: Services/Discount/Discount.Grpc/Dockerfile 48 | 49 | ordering.api: 50 | image: ${DOCKER_REGISTRY-}orderingapi 51 | build: 52 | context: . 53 | dockerfile: Services/Ordering/Ordering.API/Dockerfile 54 | 55 | ocelotapigw: 56 | image: ${DOCKER_REGISTRY-}ocelotapigw 57 | build: 58 | context: . 59 | dockerfile: ApiGateways/OcelotApiGw/Dockerfile 60 | 61 | shopping.aggregator: 62 | image: ${DOCKER_REGISTRY-}shoppingaggregator 63 | build: 64 | context: . 65 | dockerfile: ApiGateways/Shopping.Aggregator/Dockerfile 66 | 67 | aspnetrunbasics: 68 | image: ${DOCKER_REGISTRY-}aspnetrunbasics 69 | build: 70 | context: . 71 | dockerfile: WebApps/AspnetRunBasics/Dockerfile 72 | 73 | volumes: 74 | mongo_data: 75 | portainer_data: 76 | postgres_data: 77 | pgadmin_data: 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Commands/UpdateOrder/UpdateOrderCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediatR; 3 | using Microsoft.Extensions.Logging; 4 | using Ordering.Application.Contracts.Persistence; 5 | using Ordering.Application.Exceptions; 6 | using Ordering.Domain.Entities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | 14 | namespace Ordering.Application.Features.Orders.Commands.UpdateOrder 15 | { 16 | public class UpdateOrderCommandHandler : IRequestHandler 17 | { 18 | private readonly IOrderRepository _orderRepository; 19 | private readonly IMapper _mapper; 20 | private readonly ILogger _logger; 21 | 22 | public UpdateOrderCommandHandler(IOrderRepository orderRepository, IMapper mapper, ILogger logger) 23 | { 24 | _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); 25 | _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); 26 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 27 | } 28 | 29 | public async Task Handle(UpdateOrderCommand request, CancellationToken cancellationToken) 30 | { 31 | var orderToUpdate = await _orderRepository.GetByIdAsync(request.Id); 32 | if (orderToUpdate == null) 33 | { 34 | throw new NotFoundException(nameof(Order), request.Id); 35 | } 36 | 37 | _mapper.Map(request, orderToUpdate, typeof(UpdateOrderCommand), typeof(Order)); 38 | 39 | await _orderRepository.UpdateAsync(orderToUpdate); 40 | 41 | _logger.LogInformation($"Order {orderToUpdate.Id} is successfully updated."); 42 | 43 | return Unit.Value; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using Discount.API.Repositories; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | using Microsoft.OpenApi.Models; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Threading.Tasks; 14 | 15 | namespace Discount.API 16 | { 17 | public class Startup 18 | { 19 | public Startup(IConfiguration configuration) 20 | { 21 | Configuration = configuration; 22 | } 23 | 24 | public IConfiguration Configuration { get; } 25 | 26 | // This method gets called by the runtime. Use this method to add services to the container. 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | services.AddScoped(); 30 | 31 | services.AddControllers(); 32 | services.AddSwaggerGen(c => 33 | { 34 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "Discount.API", Version = "v1" }); 35 | }); 36 | } 37 | 38 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 39 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 40 | { 41 | if (env.IsDevelopment()) 42 | { 43 | app.UseDeveloperExceptionPage(); 44 | app.UseSwagger(); 45 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Discount.API v1")); 46 | } 47 | 48 | app.UseRouting(); 49 | 50 | app.UseAuthorization(); 51 | 52 | app.UseEndpoints(endpoints => 53 | { 54 | endpoints.MapControllers(); 55 | }); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Controllers/DiscountController.cs: -------------------------------------------------------------------------------- 1 | using Discount.API.Entities; 2 | using Discount.API.Repositories; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Threading.Tasks; 9 | 10 | namespace Discount.API.Controllers 11 | { 12 | [ApiController] 13 | [Route("api/v1/[controller]")] 14 | public class DiscountController : ControllerBase 15 | { 16 | private readonly IDiscountRepository _repository; 17 | 18 | public DiscountController(IDiscountRepository repository) 19 | { 20 | _repository = repository ?? throw new ArgumentNullException(nameof(repository)); 21 | } 22 | 23 | [HttpGet("{productName}", Name = "GetDiscount")] 24 | [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)] 25 | public async Task> GetDiscount(string productName) 26 | { 27 | var coupon = await _repository.GetDiscount(productName); 28 | return Ok(coupon); 29 | } 30 | 31 | [HttpPost] 32 | [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)] 33 | public async Task> CreateDiscount([FromBody] Coupon coupon) 34 | { 35 | await _repository.CreateDiscount(coupon); 36 | return CreatedAtRoute("GetDiscount", new { productName = coupon.ProductName }, coupon); 37 | } 38 | 39 | [HttpPut] 40 | [ProducesResponseType(typeof(Coupon), (int)HttpStatusCode.OK)] 41 | public async Task> UpdateDiscount([FromBody] Coupon coupon) 42 | { 43 | return Ok(await _repository.UpdateDiscount(coupon)); 44 | } 45 | 46 | [HttpDelete("{productName}", Name = "DeleteDiscount")] 47 | [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] 48 | public async Task> DeleteDiscount(string productName) 49 | { 50 | return Ok(await _repository.DeleteDiscount(productName)); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using Catalog.API.Data; 2 | using Catalog.API.Repositories; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using Microsoft.Extensions.Logging; 10 | using Microsoft.OpenApi.Models; 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | using System.Threading.Tasks; 15 | 16 | namespace Catalog.API 17 | { 18 | public class Startup 19 | { 20 | public Startup(IConfiguration configuration) 21 | { 22 | Configuration = configuration; 23 | } 24 | 25 | public IConfiguration Configuration { get; } 26 | 27 | // This method gets called by the runtime. Use this method to add services to the container. 28 | public void ConfigureServices(IServiceCollection services) 29 | { 30 | 31 | services.AddControllers(); 32 | services.AddSwaggerGen(c => 33 | { 34 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "Catalog.API", Version = "v1" }); 35 | }); 36 | 37 | services.AddScoped(); 38 | services.AddScoped(); 39 | } 40 | 41 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 42 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 43 | { 44 | if (env.IsDevelopment()) 45 | { 46 | app.UseDeveloperExceptionPage(); 47 | app.UseSwagger(); 48 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Catalog.API v1")); 49 | } 50 | 51 | app.UseRouting(); 52 | 53 | app.UseAuthorization(); 54 | 55 | app.UseEndpoints(endpoints => 56 | { 57 | endpoints.MapControllers(); 58 | }); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/ProductDetail.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using AspnetRunBasics.Models; 4 | using AspnetRunBasics.Services; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace AspnetRunBasics 9 | { 10 | public class ProductDetailModel : PageModel 11 | { 12 | private readonly ICatalogService _catalogService; 13 | private readonly IBasketService _basketService; 14 | 15 | public ProductDetailModel(ICatalogService catalogService, IBasketService basketService) 16 | { 17 | _catalogService = catalogService ?? throw new ArgumentNullException(nameof(catalogService)); 18 | _basketService = basketService ?? throw new ArgumentNullException(nameof(basketService)); 19 | } 20 | 21 | public CatalogModel Product { get; set; } 22 | 23 | [BindProperty] 24 | public string Color { get; set; } 25 | 26 | [BindProperty] 27 | public int Quantity { get; set; } 28 | 29 | public async Task OnGetAsync(string productId) 30 | { 31 | if (productId == null) 32 | { 33 | return NotFound(); 34 | } 35 | 36 | Product = await _catalogService.GetCatalog(productId); 37 | if (Product == null) 38 | { 39 | return NotFound(); 40 | } 41 | return Page(); 42 | } 43 | 44 | public async Task OnPostAddToCartAsync(string productId) 45 | { 46 | var product = await _catalogService.GetCatalog(productId); 47 | 48 | var userName = "swn"; 49 | var basket = await _basketService.GetBasket(userName); 50 | 51 | basket.Items.Add(new BasketItemModel 52 | { 53 | ProductId = productId, 54 | ProductName = product.Name, 55 | Price = product.Price, 56 | Quantity = Quantity, 57 | Color = Color 58 | }); 59 | 60 | var basketUpdated = await _basketService.UpdateBasket(basket); 61 | 62 | return RedirectToPage("Cart"); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Startup.cs: -------------------------------------------------------------------------------- 1 | using AspnetRunBasics.Services; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using System; 8 | 9 | namespace AspnetRunBasics 10 | { 11 | public class Startup 12 | { 13 | public Startup(IConfiguration configuration) 14 | { 15 | Configuration = configuration; 16 | } 17 | 18 | public IConfiguration Configuration { get; } 19 | 20 | // This method gets called by the runtime. Use this method to add services to the container. 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.AddHttpClient(c => 24 | c.BaseAddress = new Uri(Configuration["ApiSettings:GatewayAddress"])); 25 | services.AddHttpClient(c => 26 | c.BaseAddress = new Uri(Configuration["ApiSettings:GatewayAddress"])); 27 | services.AddHttpClient(c => 28 | c.BaseAddress = new Uri(Configuration["ApiSettings:GatewayAddress"])); 29 | 30 | services.AddRazorPages(); 31 | } 32 | 33 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 34 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 35 | { 36 | if (env.IsDevelopment()) 37 | { 38 | app.UseDeveloperExceptionPage(); 39 | } 40 | else 41 | { 42 | app.UseExceptionHandler("/Error"); 43 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 44 | app.UseHsts(); 45 | } 46 | 47 | app.UseHttpsRedirection(); 48 | app.UseStaticFiles(); 49 | 50 | app.UseRouting(); 51 | 52 | app.UseAuthorization(); 53 | 54 | app.UseEndpoints(endpoints => 55 | { 56 | endpoints.MapRazorPages(); 57 | }); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Order.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AspnetRunBasics.OrderModel 3 | @{ 4 | ViewData["Title"] = "Order"; 5 | } 6 | 7 |
8 |
9 |
10 | 16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | @foreach (var order in Model.Orders) 38 | { 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | } 48 | 49 |
First NameLast NameEmailAddressTotalPrice
@order.FirstName@order.LastName@order.EmailAddress@order.AddressLine@order.TotalPrice $
50 |
51 |
52 |
53 |
54 |
55 | Continue Shopping 56 |
57 |
58 |
59 |
60 |
61 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using Microsoft.Extensions.Logging; 8 | using Microsoft.OpenApi.Models; 9 | using Shopping.Aggregator.Services; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Threading.Tasks; 14 | 15 | namespace Shopping.Aggregator 16 | { 17 | public class Startup 18 | { 19 | public Startup(IConfiguration configuration) 20 | { 21 | Configuration = configuration; 22 | } 23 | 24 | public IConfiguration Configuration { get; } 25 | 26 | // This method gets called by the runtime. Use this method to add services to the container. 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | services.AddHttpClient(c => 30 | c.BaseAddress = new Uri(Configuration["ApiSettings:CatalogUrl"])); 31 | 32 | services.AddHttpClient(c => 33 | c.BaseAddress = new Uri(Configuration["ApiSettings:BasketUrl"])); 34 | 35 | services.AddHttpClient(c => 36 | c.BaseAddress = new Uri(Configuration["ApiSettings:OrderingUrl"])); 37 | 38 | services.AddControllers(); 39 | services.AddSwaggerGen(c => 40 | { 41 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "Shopping.Aggregator", Version = "v1" }); 42 | }); 43 | } 44 | 45 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 46 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 47 | { 48 | if (env.IsDevelopment()) 49 | { 50 | app.UseDeveloperExceptionPage(); 51 | app.UseSwagger(); 52 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Shopping.Aggregator v1")); 53 | } 54 | 55 | app.UseRouting(); 56 | 57 | app.UseAuthorization(); 58 | 59 | app.UseEndpoints(endpoints => 60 | { 61 | endpoints.MapControllers(); 62 | }); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Extensions/HostExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Data.SqlClient; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | 8 | namespace Ordering.API.Extensions 9 | { 10 | public static class HostExtensions 11 | { 12 | public static IHost MigrateDatabase(this IHost host, 13 | Action seeder, 14 | int? retry = 0) where TContext : DbContext 15 | { 16 | int retryForAvailability = retry.Value; 17 | 18 | using (var scope = host.Services.CreateScope()) 19 | { 20 | var services = scope.ServiceProvider; 21 | var logger = services.GetRequiredService>(); 22 | var context = services.GetService(); 23 | 24 | try 25 | { 26 | logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name); 27 | 28 | InvokeSeeder(seeder, context, services); 29 | 30 | logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); 31 | } 32 | catch (SqlException ex) 33 | { 34 | logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); 35 | 36 | if (retryForAvailability < 50) 37 | { 38 | retryForAvailability++; 39 | System.Threading.Thread.Sleep(2000); 40 | MigrateDatabase(host, seeder, retryForAvailability); 41 | } 42 | } 43 | } 44 | return host; 45 | } 46 | 47 | private static void InvokeSeeder(Action seeder, 48 | TContext context, 49 | IServiceProvider services) 50 | where TContext : DbContext 51 | { 52 | context.Database.Migrate(); 53 | seeder(context, services); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Application/Features/Orders/Commands/CheckoutOrder/CheckoutOrderCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using MediatR; 3 | using Microsoft.Extensions.Logging; 4 | using Ordering.Application.Contracts.Infrastructure; 5 | using Ordering.Application.Contracts.Persistence; 6 | using Ordering.Application.Models; 7 | using Ordering.Domain.Entities; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace Ordering.Application.Features.Orders.Commands.CheckoutOrder 16 | { 17 | public class CheckoutOrderCommandHandler : IRequestHandler 18 | { 19 | private readonly IOrderRepository _orderRepository; 20 | private readonly IMapper _mapper; 21 | private readonly IEmailService _emailService; 22 | private readonly ILogger _logger; 23 | 24 | public CheckoutOrderCommandHandler(IOrderRepository orderRepository, IMapper mapper, IEmailService emailService, ILogger logger) 25 | { 26 | _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); 27 | _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); 28 | _emailService = emailService ?? throw new ArgumentNullException(nameof(emailService)); 29 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 30 | } 31 | 32 | public async Task Handle(CheckoutOrderCommand request, CancellationToken cancellationToken) 33 | { 34 | var orderEntity = _mapper.Map(request); 35 | var newOrder = await _orderRepository.AddAsync(orderEntity); 36 | 37 | _logger.LogInformation($"Order {newOrder.Id} is successfully created."); 38 | 39 | await SendMail(newOrder); 40 | 41 | return newOrder.Id; 42 | } 43 | 44 | private async Task SendMail(Order order) 45 | { 46 | var email = new Email() { To = "ezozkme@gmail.com", Body = $"Order was created.", Subject = "Order was created" }; 47 | 48 | try 49 | { 50 | await _emailService.SendEmail(email); 51 | } 52 | catch (Exception ex) 53 | { 54 | _logger.LogError($"Order {order.Id} failed due to an error with the mail service: {ex.Message}"); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Product.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using AspnetRunBasics.Models; 6 | using AspnetRunBasics.Services; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | 10 | namespace AspnetRunBasics 11 | { 12 | public class ProductModel : PageModel 13 | { 14 | private readonly ICatalogService _catalogService; 15 | private readonly IBasketService _basketService; 16 | 17 | public ProductModel(ICatalogService catalogService, IBasketService basketService) 18 | { 19 | _catalogService = catalogService ?? throw new ArgumentNullException(nameof(catalogService)); 20 | _basketService = basketService ?? throw new ArgumentNullException(nameof(basketService)); 21 | } 22 | 23 | public IEnumerable CategoryList { get; set; } = new List(); 24 | public IEnumerable ProductList { get; set; } = new List(); 25 | 26 | 27 | [BindProperty(SupportsGet = true)] 28 | public string SelectedCategory { get; set; } 29 | 30 | public async Task OnGetAsync(string categoryName) 31 | { 32 | var productList = await _catalogService.GetCatalog(); 33 | CategoryList = productList.Select(p => p.Category).Distinct(); 34 | 35 | if (!string.IsNullOrWhiteSpace(categoryName)) 36 | { 37 | ProductList = productList.Where(p => p.Category == categoryName); 38 | SelectedCategory = categoryName; 39 | } 40 | else 41 | { 42 | ProductList = productList; 43 | } 44 | 45 | return Page(); 46 | } 47 | 48 | public async Task OnPostAddToCartAsync(string productId) 49 | { 50 | var product = await _catalogService.GetCatalog(productId); 51 | 52 | var userName = "swn"; 53 | var basket = await _basketService.GetBasket(userName); 54 | 55 | basket.Items.Add(new BasketItemModel 56 | { 57 | ProductId = productId, 58 | ProductName = product.Name, 59 | Price = product.Price, 60 | Quantity = 1, 61 | Color = "Black" 62 | }); 63 | 64 | var basketUpdated = await _basketService.UpdateBasket(basket); 65 | 66 | return RedirectToPage("Cart"); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Controllers/OrderController.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Ordering.Application.Features.Orders.Commands.CheckoutOrder; 5 | using Ordering.Application.Features.Orders.Commands.DeleteOrder; 6 | using Ordering.Application.Features.Orders.Commands.UpdateOrder; 7 | using Ordering.Application.Features.Orders.Queries.GetOrdersList; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Net; 12 | using System.Threading.Tasks; 13 | 14 | namespace Ordering.API.Controllers 15 | { 16 | [ApiController] 17 | [Route("api/v1/[controller]")] 18 | public class OrderController : ControllerBase 19 | { 20 | private readonly IMediator _mediator; 21 | 22 | public OrderController(IMediator mediator) 23 | { 24 | _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); 25 | } 26 | 27 | [HttpGet("{userName}", Name = "GetOrder")] 28 | [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] 29 | public async Task>> GetOrdersByUserName(string userName) 30 | { 31 | var query = new GetOrdersListQuery(userName); 32 | var orders = await _mediator.Send(query); 33 | return Ok(orders); 34 | } 35 | 36 | // testing purpose 37 | [HttpPost(Name = "CheckoutOrder")] 38 | [ProducesResponseType((int)HttpStatusCode.OK)] 39 | public async Task> CheckoutOrder([FromBody] CheckoutOrderCommand command) 40 | { 41 | var result = await _mediator.Send(command); 42 | return Ok(result); 43 | } 44 | 45 | [HttpPut(Name = "UpdateOrder")] 46 | [ProducesResponseType(StatusCodes.Status204NoContent)] 47 | [ProducesResponseType(StatusCodes.Status404NotFound)] 48 | [ProducesDefaultResponseType] 49 | public async Task UpdateOrder([FromBody] UpdateOrderCommand command) 50 | { 51 | await _mediator.Send(command); 52 | return NoContent(); 53 | } 54 | 55 | [HttpDelete("{id}", Name = "DeleteOrder")] 56 | [ProducesResponseType(StatusCodes.Status204NoContent)] 57 | [ProducesResponseType(StatusCodes.Status404NotFound)] 58 | [ProducesDefaultResponseType] 59 | public async Task DeleteOrder(int id) 60 | { 61 | var command = new DeleteOrderCommand() { Id = id }; 62 | await _mediator.Send(command); 63 | return NoContent(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Infrastructure/Migrations/20210319130102_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace Ordering.Infrastructure.Migrations 5 | { 6 | public partial class InitialCreate : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "Orders", 12 | columns: table => new 13 | { 14 | Id = table.Column(type: "int", nullable: false) 15 | .Annotation("SqlServer:Identity", "1, 1"), 16 | UserName = table.Column(type: "nvarchar(max)", nullable: true), 17 | TotalPrice = table.Column(type: "decimal(18,2)", nullable: false), 18 | FirstName = table.Column(type: "nvarchar(max)", nullable: true), 19 | LastName = table.Column(type: "nvarchar(max)", nullable: true), 20 | EmailAddress = table.Column(type: "nvarchar(max)", nullable: true), 21 | AddressLine = table.Column(type: "nvarchar(max)", nullable: true), 22 | Country = table.Column(type: "nvarchar(max)", nullable: true), 23 | State = table.Column(type: "nvarchar(max)", nullable: true), 24 | ZipCode = table.Column(type: "nvarchar(max)", nullable: true), 25 | CardName = table.Column(type: "nvarchar(max)", nullable: true), 26 | CardNumber = table.Column(type: "nvarchar(max)", nullable: true), 27 | Expiration = table.Column(type: "nvarchar(max)", nullable: true), 28 | CVV = table.Column(type: "nvarchar(max)", nullable: true), 29 | PaymentMethod = table.Column(type: "int", nullable: false), 30 | CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), 31 | CreatedDate = table.Column(type: "datetime2", nullable: false), 32 | LastModifiedBy = table.Column(type: "nvarchar(max)", nullable: true), 33 | LastModifiedDate = table.Column(type: "datetime2", nullable: true) 34 | }, 35 | constraints: table => 36 | { 37 | table.PrimaryKey("PK_Orders", x => x.Id); 38 | }); 39 | } 40 | 41 | protected override void Down(MigrationBuilder migrationBuilder) 42 | { 43 | migrationBuilder.DropTable( 44 | name: "Orders"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ApiGateways/Shopping.Aggregator/Controllers/ShoppingController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Shopping.Aggregator.Models; 3 | using Shopping.Aggregator.Services; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Threading.Tasks; 9 | 10 | namespace Shopping.Aggregator.Controllers 11 | { 12 | [ApiController] 13 | [Route("api/v1/[controller]")] 14 | public class ShoppingController : ControllerBase 15 | { 16 | private readonly ICatalogService _catalogService; 17 | private readonly IBasketService _basketService; 18 | private readonly IOrderService _orderService; 19 | 20 | public ShoppingController(ICatalogService catalogService, IBasketService basketService, IOrderService orderService) 21 | { 22 | _catalogService = catalogService ?? throw new ArgumentNullException(nameof(catalogService)); 23 | _basketService = basketService ?? throw new ArgumentNullException(nameof(basketService)); 24 | _orderService = orderService ?? throw new ArgumentNullException(nameof(orderService)); 25 | } 26 | 27 | [HttpGet("{userName}", Name = "GetShopping")] 28 | [ProducesResponseType(typeof(ShoppingModel), (int)HttpStatusCode.OK)] 29 | public async Task> GetShopping(string userName) 30 | { 31 | // get basket with username 32 | // iterate basket items and consume products with basket item productId member 33 | // map product related members into basketitem dto with extended columns 34 | // consume ordering microservices in order to retrieve order list 35 | // return root ShoppngModel dto class which including all responses 36 | 37 | var basket = await _basketService.GetBasket(userName); 38 | 39 | foreach (var item in basket.Items) 40 | { 41 | var product = await _catalogService.GetCatalog(item.ProductId); 42 | 43 | // set additional product fields onto basket item 44 | item.ProductName = product.Name; 45 | item.Category = product.Category; 46 | item.Summary = product.Summary; 47 | item.Description = product.Description; 48 | item.ImageFile = product.ImageFile; 49 | } 50 | 51 | var orders = await _orderService.GetOrdersByUserName(userName); 52 | 53 | var shoppingModel = new ShoppingModel 54 | { 55 | UserName = userName, 56 | BasketWithProducts = basket, 57 | Orders = orders 58 | }; 59 | 60 | return Ok(shoppingModel); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Services/Basket/Basket.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using Basket.API.GrpcServices; 2 | using Basket.API.Repositories; 3 | using Discount.Grpc.Protos; 4 | using MassTransit; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.OpenApi.Models; 11 | using System; 12 | 13 | namespace Basket.API 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | // Redis Configuration 28 | services.AddStackExchangeRedisCache(options => 29 | { 30 | options.Configuration = Configuration.GetValue("CacheSettings:ConnectionString"); 31 | }); 32 | 33 | // General Configuration 34 | services.AddScoped(); 35 | services.AddAutoMapper(typeof(Startup)); 36 | 37 | // Grpc Configuration 38 | services.AddGrpcClient 39 | (o => o.Address = new Uri(Configuration["GrpcSettings:DiscountUrl"])); 40 | services.AddScoped(); 41 | 42 | // MassTransit-RabbitMQ Configuration 43 | services.AddMassTransit(config => { 44 | config.UsingRabbitMq((ctx, cfg) => { 45 | cfg.Host(Configuration["EventBusSettings:HostAddress"]); 46 | }); 47 | }); 48 | services.AddMassTransitHostedService(); 49 | 50 | 51 | services.AddControllers(); 52 | services.AddSwaggerGen(c => 53 | { 54 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "Basket.API", Version = "v1" }); 55 | }); 56 | } 57 | 58 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 59 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 60 | { 61 | if (env.IsDevelopment()) 62 | { 63 | app.UseDeveloperExceptionPage(); 64 | app.UseSwagger(); 65 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Basket.API v1")); 66 | } 67 | 68 | app.UseRouting(); 69 | 70 | app.UseAuthorization(); 71 | 72 | app.UseEndpoints(endpoints => 73 | { 74 | endpoints.MapControllers(); 75 | }); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Extensions/HostExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using Npgsql; 6 | 7 | namespace Discount.API.Extensions 8 | { 9 | public static class HostExtensions 10 | { 11 | public static IHost MigrateDatabase(this IHost host, int? retry = 0) 12 | { 13 | int retryForAvailability = retry.Value; 14 | 15 | using (var scope = host.Services.CreateScope()) 16 | { 17 | var services = scope.ServiceProvider; 18 | var configuration = services.GetRequiredService(); 19 | var logger = services.GetRequiredService>(); 20 | 21 | try 22 | { 23 | logger.LogInformation("Migrating postresql database."); 24 | 25 | using var connection = new NpgsqlConnection 26 | (configuration.GetValue("DatabaseSettings:ConnectionString")); 27 | connection.Open(); 28 | 29 | using var command = new NpgsqlCommand 30 | { 31 | Connection = connection 32 | }; 33 | 34 | command.CommandText = "DROP TABLE IF EXISTS Coupon"; 35 | command.ExecuteNonQuery(); 36 | 37 | command.CommandText = @"CREATE TABLE Coupon(Id SERIAL PRIMARY KEY, 38 | ProductName VARCHAR(24) NOT NULL, 39 | Description TEXT, 40 | Amount INT)"; 41 | command.ExecuteNonQuery(); 42 | 43 | command.CommandText = "INSERT INTO Coupon(ProductName, Description, Amount) VALUES('IPhone X', 'IPhone Discount', 150);"; 44 | command.ExecuteNonQuery(); 45 | 46 | command.CommandText = "INSERT INTO Coupon(ProductName, Description, Amount) VALUES('Samsung 10', 'Samsung Discount', 100);"; 47 | command.ExecuteNonQuery(); 48 | 49 | logger.LogInformation("Migrated postresql database."); 50 | } 51 | catch (NpgsqlException ex) 52 | { 53 | logger.LogError(ex, "An error occurred while migrating the postresql database"); 54 | 55 | if (retryForAvailability < 50) 56 | { 57 | retryForAvailability++; 58 | System.Threading.Thread.Sleep(2000); 59 | MigrateDatabase(host, retryForAvailability); 60 | } 61 | } 62 | } 63 | 64 | return host; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Extensions/HostExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using Npgsql; 6 | 7 | namespace Discount.Grpc.Extensions 8 | { 9 | public static class HostExtensions 10 | { 11 | public static IHost MigrateDatabase(this IHost host, int? retry = 0) 12 | { 13 | int retryForAvailability = retry.Value; 14 | 15 | using (var scope = host.Services.CreateScope()) 16 | { 17 | var services = scope.ServiceProvider; 18 | var configuration = services.GetRequiredService(); 19 | var logger = services.GetRequiredService>(); 20 | 21 | try 22 | { 23 | logger.LogInformation("Migrating postresql database."); 24 | 25 | using var connection = new NpgsqlConnection 26 | (configuration.GetValue("DatabaseSettings:ConnectionString")); 27 | connection.Open(); 28 | 29 | using var command = new NpgsqlCommand 30 | { 31 | Connection = connection 32 | }; 33 | 34 | command.CommandText = "DROP TABLE IF EXISTS Coupon"; 35 | command.ExecuteNonQuery(); 36 | 37 | command.CommandText = @"CREATE TABLE Coupon(Id SERIAL PRIMARY KEY, 38 | ProductName VARCHAR(24) NOT NULL, 39 | Description TEXT, 40 | Amount INT)"; 41 | command.ExecuteNonQuery(); 42 | 43 | command.CommandText = "INSERT INTO Coupon(ProductName, Description, Amount) VALUES('IPhone X', 'IPhone Discount', 150);"; 44 | command.ExecuteNonQuery(); 45 | 46 | command.CommandText = "INSERT INTO Coupon(ProductName, Description, Amount) VALUES('Samsung 10', 'Samsung Discount', 100);"; 47 | command.ExecuteNonQuery(); 48 | 49 | logger.LogInformation("Migrated postresql database."); 50 | } 51 | catch (NpgsqlException ex) 52 | { 53 | logger.LogError(ex, "An error occurred while migrating the postresql database"); 54 | 55 | if (retryForAvailability < 50) 56 | { 57 | retryForAvailability++; 58 | System.Threading.Thread.Sleep(2000); 59 | MigrateDatabase(host, retryForAvailability); 60 | } 61 | } 62 | } 63 | 64 | return host; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using EventBus.Messages.Common; 2 | using MassTransit; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using Microsoft.Extensions.Logging; 10 | using Microsoft.OpenApi.Models; 11 | using Ordering.API.EventBusConsumer; 12 | using Ordering.Application; 13 | using Ordering.Infrastructure; 14 | using System; 15 | using System.Collections.Generic; 16 | using System.Linq; 17 | using System.Threading.Tasks; 18 | 19 | namespace Ordering.API 20 | { 21 | public class Startup 22 | { 23 | public Startup(IConfiguration configuration) 24 | { 25 | Configuration = configuration; 26 | } 27 | 28 | public IConfiguration Configuration { get; } 29 | 30 | // This method gets called by the runtime. Use this method to add services to the container. 31 | public void ConfigureServices(IServiceCollection services) 32 | { 33 | services.AddApplicationServices(); 34 | services.AddInfrastructureServices(Configuration); 35 | 36 | // MassTransit-RabbitMQ Configuration 37 | services.AddMassTransit(config => { 38 | 39 | config.AddConsumer(); 40 | 41 | config.UsingRabbitMq((ctx, cfg) => { 42 | cfg.Host(Configuration["EventBusSettings:HostAddress"]); 43 | 44 | cfg.ReceiveEndpoint(EventBusConstants.BasketCheckoutQueue, c => 45 | { 46 | c.ConfigureConsumer(ctx); 47 | }); 48 | }); 49 | }); 50 | services.AddMassTransitHostedService(); 51 | 52 | // General Configuration 53 | services.AddAutoMapper(typeof(Startup)); 54 | services.AddScoped(); 55 | 56 | services.AddControllers(); 57 | services.AddSwaggerGen(c => 58 | { 59 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "Ordering.API", Version = "v1" }); 60 | }); 61 | } 62 | 63 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 64 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 65 | { 66 | if (env.IsDevelopment()) 67 | { 68 | app.UseDeveloperExceptionPage(); 69 | app.UseSwagger(); 70 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ordering.API v1")); 71 | } 72 | 73 | app.UseRouting(); 74 | 75 | app.UseAuthorization(); 76 | 77 | app.UseEndpoints(endpoints => 78 | { 79 | endpoints.MapControllers(); 80 | }); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Repositories/ProductRepository.cs: -------------------------------------------------------------------------------- 1 | using Catalog.API.Data; 2 | using Catalog.API.Entities; 3 | using MongoDB.Driver; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace Catalog.API.Repositories 10 | { 11 | public class ProductRepository : IProductRepository 12 | { 13 | private readonly ICatalogContext _context; 14 | 15 | public ProductRepository(ICatalogContext context) 16 | { 17 | _context = context ?? throw new ArgumentNullException(nameof(context)); 18 | } 19 | 20 | public async Task> GetProducts() 21 | { 22 | return await _context 23 | .Products 24 | .Find(p => true) 25 | .ToListAsync(); 26 | } 27 | public async Task GetProduct(string id) 28 | { 29 | return await _context 30 | .Products 31 | .Find(p => p.Id == id) 32 | .FirstOrDefaultAsync(); 33 | } 34 | 35 | public async Task> GetProductByName(string name) 36 | { 37 | FilterDefinition filter = Builders.Filter.Eq(p => p.Name, name); 38 | 39 | return await _context 40 | .Products 41 | .Find(filter) 42 | .ToListAsync(); 43 | } 44 | 45 | public async Task> GetProductByCategory(string categoryName) 46 | { 47 | FilterDefinition filter = Builders.Filter.Eq(p => p.Category, categoryName); 48 | 49 | return await _context 50 | .Products 51 | .Find(filter) 52 | .ToListAsync(); 53 | } 54 | 55 | public async Task CreateProduct(Product product) 56 | { 57 | await _context.Products.InsertOneAsync(product); 58 | } 59 | 60 | public async Task UpdateProduct(Product product) 61 | { 62 | var updateResult = await _context 63 | .Products 64 | .ReplaceOneAsync(filter: g => g.Id == product.Id, replacement: product); 65 | 66 | return updateResult.IsAcknowledged 67 | && updateResult.ModifiedCount > 0; 68 | } 69 | 70 | public async Task DeleteProduct(string id) 71 | { 72 | FilterDefinition filter = Builders.Filter.Eq(p => p.Id, id); 73 | 74 | DeleteResult deleteResult = await _context 75 | .Products 76 | .DeleteOneAsync(filter); 77 | 78 | return deleteResult.IsAcknowledged 79 | && deleteResult.DeletedCount > 0; 80 | } 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AspnetRunBasics.ContactModel 3 | @{ 4 | ViewData["Title"] = "Contact"; 5 | } 6 | 7 |
8 |
9 |

E-COMMERCE CONTACT

10 |

Contact Page build with Bootstrap 4 !

11 |
12 |
13 |
14 |
15 |
16 | 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Contact us. 31 |
32 |
33 |
34 |
35 | 36 | 37 |
38 |
39 | 40 | 41 | We'll never share your email with anyone else. 42 |
43 |
44 | 45 | 46 |
47 |
48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
Address
57 |
58 |

3 rue des Champs Elysées

59 |

75008 PARIS

60 |

France

61 |

Email : email@example.com

62 |

Tel. +33 12 56 11 51 84

63 | 64 |
65 | 66 |
67 |
68 |
69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.API/Repositories/DiscountRepository.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using Discount.API.Entities; 3 | using Microsoft.Extensions.Configuration; 4 | using Npgsql; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace Discount.API.Repositories 9 | { 10 | public class DiscountRepository : IDiscountRepository 11 | { 12 | private readonly IConfiguration _configuration; 13 | 14 | public DiscountRepository(IConfiguration configuration) 15 | { 16 | _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); 17 | } 18 | 19 | public async Task GetDiscount(string productName) 20 | { 21 | using var connection = new NpgsqlConnection 22 | (_configuration.GetValue("DatabaseSettings:ConnectionString")); 23 | 24 | var coupon = await connection.QueryFirstOrDefaultAsync 25 | ("SELECT * FROM Coupon WHERE ProductName = @ProductName", new { ProductName = productName }); 26 | 27 | if (coupon == null) 28 | return new Coupon 29 | { ProductName = "No Discount", Amount = 0, Description = "No Discount Desc" }; 30 | 31 | return coupon; 32 | } 33 | 34 | public async Task CreateDiscount(Coupon coupon) 35 | { 36 | using var connection = new NpgsqlConnection 37 | (_configuration.GetValue("DatabaseSettings:ConnectionString")); 38 | 39 | var affected = 40 | await connection.ExecuteAsync 41 | ("INSERT INTO Coupon (ProductName, Description, Amount) VALUES (@ProductName, @Description, @Amount)", 42 | new { ProductName = coupon.ProductName, Description = coupon.Description, Amount = coupon.Amount }); 43 | 44 | if (affected == 0) 45 | return false; 46 | 47 | return true; 48 | } 49 | 50 | public async Task UpdateDiscount(Coupon coupon) 51 | { 52 | using var connection = new NpgsqlConnection(_configuration.GetValue("DatabaseSettings:ConnectionString")); 53 | 54 | var affected = await connection.ExecuteAsync 55 | ("UPDATE Coupon SET ProductName=@ProductName, Description = @Description, Amount = @Amount WHERE Id = @Id", 56 | new { ProductName = coupon.ProductName, Description = coupon.Description, Amount = coupon.Amount, Id = coupon.Id }); 57 | 58 | if (affected == 0) 59 | return false; 60 | 61 | return true; 62 | } 63 | 64 | public async Task DeleteDiscount(string productName) 65 | { 66 | using var connection = new NpgsqlConnection(_configuration.GetValue("DatabaseSettings:ConnectionString")); 67 | 68 | var affected = await connection.ExecuteAsync("DELETE FROM Coupon WHERE ProductName = @ProductName", 69 | new { ProductName = productName }); 70 | 71 | if (affected == 0) 72 | return false; 73 | 74 | return true; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Services/DiscountService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Discount.Grpc.Entities; 3 | using Discount.Grpc.Protos; 4 | using Discount.Grpc.Repositories; 5 | using Grpc.Core; 6 | using Microsoft.Extensions.Logging; 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | namespace Discount.Grpc.Services 11 | { 12 | public class DiscountService : DiscountProtoService.DiscountProtoServiceBase 13 | { 14 | private readonly IDiscountRepository _repository; 15 | private readonly IMapper _mapper; 16 | private readonly ILogger _logger; 17 | 18 | public DiscountService(IDiscountRepository repository, IMapper mapper, ILogger logger) 19 | { 20 | _repository = repository ?? throw new ArgumentNullException(nameof(repository)); 21 | _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); 22 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 23 | } 24 | 25 | public override async Task GetDiscount(GetDiscountRequest request, ServerCallContext context) 26 | { 27 | var coupon = await _repository.GetDiscount(request.ProductName); 28 | if (coupon == null) 29 | { 30 | throw new RpcException(new Status(StatusCode.NotFound, $"Discount with ProductName={request.ProductName} is not found.")); 31 | } 32 | _logger.LogInformation("Discount is retrieved for ProductName : {productName}, Amount : {amount}", coupon.ProductName, coupon.Amount); 33 | 34 | var couponModel = _mapper.Map(coupon); 35 | return couponModel; 36 | } 37 | 38 | public override async Task CreateDiscount(CreateDiscountRequest request, ServerCallContext context) 39 | { 40 | var coupon = _mapper.Map(request.Coupon); 41 | 42 | await _repository.CreateDiscount(coupon); 43 | _logger.LogInformation("Discount is successfully created. ProductName : {ProductName}", coupon.ProductName); 44 | 45 | var couponModel = _mapper.Map(coupon); 46 | return couponModel; 47 | } 48 | 49 | public override async Task UpdateDiscount(UpdateDiscountRequest request, ServerCallContext context) 50 | { 51 | var coupon = _mapper.Map(request.Coupon); 52 | 53 | await _repository.UpdateDiscount(coupon); 54 | _logger.LogInformation("Discount is successfully updated. ProductName : {ProductName}", coupon.ProductName); 55 | 56 | var couponModel = _mapper.Map(coupon); 57 | return couponModel; 58 | } 59 | 60 | public override async Task DeleteDiscount(DeleteDiscountRequest request, ServerCallContext context) 61 | { 62 | var deleted = await _repository.DeleteDiscount(request.ProductName); 63 | var response = new DeleteDiscountResponse 64 | { 65 | Success = deleted 66 | }; 67 | 68 | return response; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Services/Discount/Discount.Grpc/Repositories/DiscountRepository.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using Discount.Grpc.Entities; 3 | using Microsoft.Extensions.Configuration; 4 | using Npgsql; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace Discount.Grpc.Repositories 9 | { 10 | public class DiscountRepository : IDiscountRepository 11 | { 12 | private readonly IConfiguration _configuration; 13 | 14 | public DiscountRepository(IConfiguration configuration) 15 | { 16 | _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); 17 | } 18 | 19 | public async Task GetDiscount(string productName) 20 | { 21 | using var connection = new NpgsqlConnection 22 | (_configuration.GetValue("DatabaseSettings:ConnectionString")); 23 | 24 | var coupon = await connection.QueryFirstOrDefaultAsync 25 | ("SELECT * FROM Coupon WHERE ProductName = @ProductName", new { ProductName = productName }); 26 | 27 | if (coupon == null) 28 | return new Coupon 29 | { ProductName = "No Discount", Amount = 0, Description = "No Discount Desc" }; 30 | 31 | return coupon; 32 | } 33 | 34 | public async Task CreateDiscount(Coupon coupon) 35 | { 36 | using var connection = new NpgsqlConnection 37 | (_configuration.GetValue("DatabaseSettings:ConnectionString")); 38 | 39 | var affected = 40 | await connection.ExecuteAsync 41 | ("INSERT INTO Coupon (ProductName, Description, Amount) VALUES (@ProductName, @Description, @Amount)", 42 | new { ProductName = coupon.ProductName, Description = coupon.Description, Amount = coupon.Amount }); 43 | 44 | if (affected == 0) 45 | return false; 46 | 47 | return true; 48 | } 49 | 50 | public async Task UpdateDiscount(Coupon coupon) 51 | { 52 | using var connection = new NpgsqlConnection(_configuration.GetValue("DatabaseSettings:ConnectionString")); 53 | 54 | var affected = await connection.ExecuteAsync 55 | ("UPDATE Coupon SET ProductName=@ProductName, Description = @Description, Amount = @Amount WHERE Id = @Id", 56 | new { ProductName = coupon.ProductName, Description = coupon.Description, Amount = coupon.Amount, Id = coupon.Id }); 57 | 58 | if (affected == 0) 59 | return false; 60 | 61 | return true; 62 | } 63 | 64 | public async Task DeleteDiscount(string productName) 65 | { 66 | using var connection = new NpgsqlConnection(_configuration.GetValue("DatabaseSettings:ConnectionString")); 67 | 68 | var affected = await connection.ExecuteAsync("DELETE FROM Coupon WHERE ProductName = @ProductName", 69 | new { ProductName = productName }); 70 | 71 | if (affected == 0) 72 | return false; 73 | 74 | return true; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs: -------------------------------------------------------------------------------- 1 | using Catalog.API.Entities; 2 | using Catalog.API.Repositories; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Threading.Tasks; 10 | 11 | namespace Catalog.API.Controllers 12 | { 13 | [ApiController] 14 | [Route("api/v1/[controller]")] 15 | public class CatalogController : ControllerBase 16 | { 17 | private readonly IProductRepository _repository; 18 | private readonly ILogger _logger; 19 | 20 | public CatalogController(IProductRepository repository, ILogger logger) 21 | { 22 | _repository = repository ?? throw new ArgumentNullException(nameof(repository)); 23 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 24 | } 25 | 26 | [HttpGet] 27 | [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] 28 | public async Task>> GetProducts() 29 | { 30 | var products = await _repository.GetProducts(); 31 | return Ok(products); 32 | } 33 | 34 | [HttpGet("{id:length(24)}", Name = "GetProduct")] 35 | [ProducesResponseType((int)HttpStatusCode.NotFound)] 36 | [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)] 37 | public async Task> GetProductById(string id) 38 | { 39 | var product = await _repository.GetProduct(id); 40 | if (product == null) 41 | { 42 | _logger.LogError($"Product with id: {id}, not found."); 43 | return NotFound(); 44 | } 45 | return Ok(product); 46 | } 47 | 48 | [Route("[action]/{category}", Name = "GetProductByCategory")] 49 | [HttpGet] 50 | [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] 51 | public async Task>> GetProductByCategory(string category) 52 | { 53 | var products = await _repository.GetProductByCategory(category); 54 | return Ok(products); 55 | } 56 | 57 | [HttpPost] 58 | [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)] 59 | public async Task> CreateProduct([FromBody] Product product) 60 | { 61 | await _repository.CreateProduct(product); 62 | 63 | return CreatedAtRoute("GetProduct", new { id = product.Id }, product); 64 | } 65 | 66 | [HttpPut] 67 | [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)] 68 | public async Task UpdateProduct([FromBody] Product product) 69 | { 70 | return Ok(await _repository.UpdateProduct(product)); 71 | } 72 | 73 | [HttpDelete("{id:length(24)}", Name = "DeleteProduct")] 74 | [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)] 75 | public async Task DeleteProductById(string id) 76 | { 77 | return Ok(await _repository.DeleteProduct(id)); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Services/Ordering/Ordering.Infrastructure/Repositories/RepositoryBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Ordering.Application.Contracts.Persistence; 3 | using Ordering.Domain.Common; 4 | using Ordering.Infrastructure.Persistence; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Linq.Expressions; 9 | using System.Threading.Tasks; 10 | 11 | namespace Ordering.Infrastructure.Repositories 12 | { 13 | public class RepositoryBase : IAsyncRepository where T : EntityBase 14 | { 15 | protected readonly OrderContext _dbContext; 16 | 17 | public RepositoryBase(OrderContext dbContext) 18 | { 19 | _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); 20 | } 21 | 22 | public async Task> GetAllAsync() 23 | { 24 | return await _dbContext.Set().ToListAsync(); 25 | } 26 | 27 | public async Task> GetAsync(Expression> predicate) 28 | { 29 | return await _dbContext.Set().Where(predicate).ToListAsync(); 30 | } 31 | 32 | public async Task> GetAsync(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, string includeString = null, bool disableTracking = true) 33 | { 34 | IQueryable query = _dbContext.Set(); 35 | if (disableTracking) query = query.AsNoTracking(); 36 | 37 | if (!string.IsNullOrWhiteSpace(includeString)) query = query.Include(includeString); 38 | 39 | if (predicate != null) query = query.Where(predicate); 40 | 41 | if (orderBy != null) 42 | return await orderBy(query).ToListAsync(); 43 | return await query.ToListAsync(); 44 | } 45 | 46 | public async Task> GetAsync(Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, List>> includes = null, bool disableTracking = true) 47 | { 48 | IQueryable query = _dbContext.Set(); 49 | if (disableTracking) query = query.AsNoTracking(); 50 | 51 | if (includes != null) query = includes.Aggregate(query, (current, include) => current.Include(include)); 52 | 53 | if (predicate != null) query = query.Where(predicate); 54 | 55 | if (orderBy != null) 56 | return await orderBy(query).ToListAsync(); 57 | return await query.ToListAsync(); 58 | } 59 | 60 | public virtual async Task GetByIdAsync(int id) 61 | { 62 | return await _dbContext.Set().FindAsync(id); 63 | } 64 | 65 | public async Task AddAsync(T entity) 66 | { 67 | _dbContext.Set().Add(entity); 68 | await _dbContext.SaveChangesAsync(); 69 | return entity; 70 | } 71 | 72 | public async Task UpdateAsync(T entity) 73 | { 74 | _dbContext.Entry(entity).State = EntityState.Modified; 75 | await _dbContext.SaveChangesAsync(); 76 | } 77 | 78 | public async Task DeleteAsync(T entity) 79 | { 80 | _dbContext.Set().Remove(entity); 81 | await _dbContext.SaveChangesAsync(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @{ 4 | ViewData["Title"] = "Home page"; 5 | } 6 | 7 |
8 | 9 |
10 |
11 |
12 | 33 |
34 | 35 | 36 | 37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 | Last products 46 |
47 |
48 |
49 | 50 | @foreach (var product in Model.ProductList.Take(4)) 51 | { 52 |
53 | 54 |
55 | } 56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 |
65 |
66 |
67 |
68 |
69 | Best products 70 |
71 |
72 |
73 | 74 | @foreach (var product in Model.ProductList.TakeLast(4)) 75 | { 76 |
77 | 78 |
79 | } 80 | 81 |
82 |
83 |
84 |
85 |
86 |
87 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Product.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AspnetRunBasics.ProductModel 3 | @{ 4 | ViewData["Title"] = "Product"; 5 | } 6 | 7 |
8 |
9 |
10 | 17 |
18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 |
Categories
26 |
    27 | 28 | @foreach (var category in Model.CategoryList) 29 | { 30 |
  • @category
  • 31 | } 32 | 33 |
34 |
35 | 36 |
37 |
Last product
38 |
39 | 40 |
@Model.ProductList.LastOrDefault().Name
41 |

@Model.ProductList.LastOrDefault().Summary

42 |

@Model.ProductList.LastOrDefault().Price $

43 |
44 |
45 | 46 |
47 | 48 |
49 |
50 | 51 | @foreach (var product in Model.ProductList) 52 | { 53 |
54 | 55 |
56 | } 57 | 58 |
59 | 74 |
75 |
76 |
77 | 78 |
79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /src/WebApps/AspnetRunBasics/Pages/Cart.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AspnetRunBasics.CartModel 3 | @{ 4 | ViewData["Title"] = "Cart"; 5 | } 6 | 7 |
8 |
9 |
10 | 16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | @foreach (var cartItem in Model.Cart.Items) 38 | { 39 | 40 | 41 | 42 | 43 | 44 | 45 | 53 | 54 | } 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
ProductAvailableQuantityPrice
@cartItem.ProductNameIn stock@(cartItem.Price * cartItem.Quantity) $ 46 | 47 |
48 | 49 | 50 |
51 | 52 |
Total@Model.Cart.TotalPrice $
66 |
67 |
68 |
69 |
70 |
71 | @**@ 72 | Continue Shopping 73 |
74 |
75 | @**@ 76 | CheckOut 77 |
78 |
79 |
80 |
81 |
82 | 83 | --------------------------------------------------------------------------------