├── .gitignore ├── LICENSE ├── README.md ├── areas-for-improvement.md ├── cheatsheet.md └── src ├── .dockerignore ├── AccountSvc ├── AccountSvc.csproj ├── Consumers │ ├── AccountInfoRequestConsumer.cs │ └── NewsletterSubscribedConsumer.cs ├── Controllers │ └── AccountController.cs ├── Dockerfile ├── Infrastructure │ └── Options │ │ └── AppConfig.cs ├── Models │ ├── Account.cs │ ├── AccountHistory.cs │ ├── Address.cs │ ├── CreateAccount.cs │ ├── PaymentInfo.cs │ ├── SignIn.cs │ ├── UpdateAccount.cs │ └── UpdatePassword.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── AccountRepository.cs │ ├── EventType.cs │ ├── IAccountRepository.cs │ └── RefType.cs ├── Services │ ├── AccountSvc.cs │ └── IAccountSvc.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── db.sql ├── AspNetMicroservices.sln ├── CatalogSvc ├── CatalogSvc.csproj ├── Consumers │ └── ProductInfoRequestConsumer.cs ├── Controllers │ └── CatalogController.cs ├── Dockerfile ├── Infrastructure │ ├── Db │ │ ├── IMongoClient.cs │ │ └── MongoClient.cs │ ├── Extensions │ │ └── ProductExtensions.cs │ └── Options │ │ └── AppConfig.cs ├── Models │ ├── Category.cs │ └── Product.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── CatalogRepository.cs │ └── ICatalogRepository.cs ├── Services │ ├── CatalogSvc.cs │ └── ICatalogSvc.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json ├── categories.json ├── db.js └── products.json ├── Microservices.Core.sln ├── Microservices.Core ├── Contracts │ ├── Account │ │ ├── AccountCreated.cs │ │ ├── AccountInfo.cs │ │ ├── AccountInfoRequest.cs │ │ └── AccountInfoResponse.cs │ ├── Base │ │ ├── CommandBase.cs │ │ └── EventBase.cs │ ├── Catalog │ │ ├── ProductInfo.cs │ │ ├── ProductInfoRequest.cs │ │ └── ProductInfoResponse.cs │ ├── Newsletter │ │ └── NewsletterSubscribed.cs │ ├── Notification │ │ └── SendMail.cs │ ├── Orders │ │ └── OrderSubmitted.cs │ ├── Payment │ │ ├── PaymentRequest.cs │ │ ├── PaymentResponse.cs │ │ └── PaymentStatus.cs │ └── Shipping │ │ ├── ShippingProvider.cs │ │ ├── ShippingRequest.cs │ │ ├── ShippingResponse.cs │ │ └── ShippingStatus.cs ├── Infrastructure │ ├── Crypt │ │ └── Crypt.cs │ ├── Extensions │ │ ├── DateTimeExtensions.cs │ │ ├── EnumExtensions.cs │ │ ├── ExceptionExtensions.cs │ │ ├── ListExtensions.cs │ │ └── StringExtensions.cs │ ├── Global.cs │ └── Options │ │ ├── EmailTemplate.cs │ │ ├── MassTransitOptions.cs │ │ ├── MongoOptions.cs │ │ ├── RedisOptions.cs │ │ └── SmtpOptions.cs ├── Microservices.Core.csproj └── README.md ├── NewsletterSvc ├── Controllers │ └── NewsletterController.cs ├── Dockerfile ├── Infrastructure │ └── Options │ │ └── AppConfig.cs ├── Models │ └── Signup.cs ├── NewsletterSvc.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── INewsletterRepository.cs │ └── NewsletterRepository.cs ├── Services │ ├── INewsletterSvc.cs │ └── NewsletterSvc.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── db.sql ├── NotificationSvc ├── Consumers │ └── SendMailConsumer.cs ├── Controllers │ └── NotificationController.cs ├── Dockerfile ├── Infrastructure │ └── Options │ │ └── AppConfig.cs ├── Models │ └── Notification.cs ├── NotificationSvc.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── INotificationRepository.cs │ └── NotificationRepository.cs ├── Services │ ├── INotificationSvc.cs │ └── NotificationSvc.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── db.sql ├── OrderSvc ├── Consumers │ ├── PaymentResponseConsumer.cs │ └── ShippingResponseConsumer.cs ├── Controllers │ └── OrderController.cs ├── Dockerfile ├── Infrastructure │ └── Options │ │ └── AppConfig.cs ├── Models │ ├── LineItem.cs │ ├── Order.cs │ ├── OrderStatus.cs │ ├── PaymentInfo.cs │ ├── PaymentMethod.cs │ ├── PaymentStatus.cs │ ├── ShippingInfo.cs │ └── ShippingStatus.cs ├── OrderSvc.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── EventType.cs │ ├── IOrderRepository.cs │ ├── OrderRepository.cs │ └── RefType.cs ├── Services │ ├── IOrderSvc.cs │ └── OrderSvc.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── db.sql ├── PaymentSvc ├── Consumers │ └── PaymentRequestConsumer.cs ├── Controllers │ └── PaymentController.cs ├── Dockerfile ├── Infrastructure │ └── Options │ │ └── AppConfig.cs ├── Models │ ├── Payment.cs │ ├── PaymentGatewayRequest.cs │ ├── PaymentGatewayResponse.cs │ ├── PaymentGatewayResponseStatus.cs │ ├── PaymentMethod.cs │ └── PaymentStatus.cs ├── PaymentSvc.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── IPaymentRepository.cs │ └── PaymentRepository.cs ├── Services │ ├── IPaymentGateway.cs │ ├── IPaymentSvc.cs │ ├── PaymentGateway.cs │ └── PaymentSvc.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── db.sql ├── RecommendationSvc ├── Consumers │ └── OrderSubmittedConsumer.cs ├── Controllers │ └── RecommendationController.cs ├── Dockerfile ├── Infrastructure │ └── Options │ │ └── AppConfig.cs ├── Models │ ├── Recommendation.cs │ └── RecommendationDto.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── RecommendationSvc.csproj ├── Repositories │ ├── IRecommendationRepository.cs │ └── RecommendationRepository.cs ├── Services │ ├── IRecommendationSvc.cs │ └── RecommendationSvc.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── db.sql ├── ShippingSvc ├── Consumers │ └── ShippingRequestConsumer.cs ├── Controllers │ └── ShippingController.cs ├── Dockerfile ├── Infrastructure │ └── Options │ │ └── AppConfig.cs ├── Models │ ├── Shipping.cs │ ├── ShippingProvider.cs │ └── ShippingStatus.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── IShippingRepository.cs │ └── ShippingRepository.cs ├── Services │ ├── IShippingSvc.cs │ └── ShippingSvc.cs ├── ShippingSvc.csproj ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── db.sql ├── Web ├── Controllers │ ├── AccountController.cs │ ├── CatalogController.cs │ ├── HomeController.cs │ ├── NewsletterController.cs │ ├── OrderController.cs │ └── RecommendationController.cs ├── Dockerfile ├── Infrastructure │ ├── Base │ │ └── ProxyBase.cs │ ├── Global │ │ └── Site.cs │ └── Options │ │ ├── AppConfig.cs │ │ ├── ServiceConfig.cs │ │ └── StoreSettings.cs ├── Models │ ├── Account │ │ ├── Account.cs │ │ ├── AccountDetails.cs │ │ ├── AccountHistory.cs │ │ ├── Address.cs │ │ ├── ChangePassword.cs │ │ └── SignIn.cs │ ├── Catalog │ │ ├── Category.cs │ │ └── Product.cs │ ├── ErrorViewModel.cs │ ├── Newsletter │ │ └── NewsletterSignUp.cs │ ├── Order │ │ ├── CartReview.cs │ │ ├── LineItem.cs │ │ ├── Order.cs │ │ ├── OrderNumberResponse.cs │ │ ├── OrderStatus.cs │ │ ├── PaymentInfo.cs │ │ ├── PaymentMethod.cs │ │ ├── PaymentStatus.cs │ │ ├── ShippingStatus.cs │ │ └── SubmitOrder.cs │ └── Recommendation │ │ └── Recommendation.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Services │ ├── AccountProxy.cs │ ├── CatalogProxy.cs │ ├── IAccountProxy.cs │ ├── ICatalogProxy.cs │ ├── INewsletterProxy.cs │ ├── IOrderProxy.cs │ ├── IRecommendationProxy.cs │ ├── NewsletterProxy.cs │ ├── OrderProxy.cs │ └── RecommendationProxy.cs ├── Startup.cs ├── Views │ ├── Account │ │ ├── AddAddress.cshtml │ │ ├── AddPayment.cshtml │ │ ├── Address.cshtml │ │ ├── Create.cshtml │ │ ├── Details.cshtml │ │ ├── EditAddress.cshtml │ │ ├── EditPayment.cshtml │ │ ├── History.cshtml │ │ ├── Index.cshtml │ │ ├── Orders.cshtml │ │ ├── Payments.cshtml │ │ ├── SignIn.cshtml │ │ ├── Update.cshtml │ │ └── UpdatePassword.cshtml │ ├── Catalog │ │ ├── Product.cshtml │ │ └── Products.cshtml │ ├── Home │ │ ├── About.cshtml │ │ ├── Cheatsheet.cshtml │ │ ├── Help.cshtml │ │ └── Index.cshtml │ ├── Order │ │ ├── Checkout.cshtml │ │ ├── Index.cshtml │ │ ├── Review.cshtml │ │ └── Submitted.cshtml │ ├── Partials │ │ ├── Account │ │ │ ├── _AccountForm.cshtml │ │ │ ├── _AddressForm.cshtml │ │ │ ├── _AddressOptions.cshtml │ │ │ ├── _Create.cshtml │ │ │ ├── _Details.cshtml │ │ │ ├── _History.cshtml │ │ │ ├── _MyAccount.cshtml │ │ │ ├── _Orders.cshtml │ │ │ ├── _PaymentForm.cshtml │ │ │ ├── _PaymentOptions.cshtml │ │ │ ├── _SignIn.cshtml │ │ │ └── _UpdatePassword.cshtml │ │ ├── Catalog │ │ │ ├── _Categories.cshtml │ │ │ ├── _FakeImg.cshtml │ │ │ ├── _FakeThumbnails.cshtml │ │ │ ├── _Product.cshtml │ │ │ └── _Products.cshtml │ │ ├── Help │ │ │ ├── _About.cshtml │ │ │ ├── _Account.cshtml │ │ │ ├── _Catalog.cshtml │ │ │ ├── _Cheatsheet.cshtml │ │ │ ├── _Help.cshtml │ │ │ ├── _Newsletter.cshtml │ │ │ ├── _Notification.cshtml │ │ │ ├── _Order.cshtml │ │ │ ├── _Payment.cshtml │ │ │ ├── _Recommendation.cshtml │ │ │ ├── _Shipping.cshtml │ │ │ └── _Web.cshtml │ │ ├── Home │ │ │ ├── _Header.cshtml │ │ │ ├── _MainContent.cshtml │ │ │ ├── _SignIn.cshtml │ │ │ └── _SignInInfo.cshtml │ │ ├── Newsletter │ │ │ └── _Newsletter.cshtml │ │ ├── Order │ │ │ ├── _Cart.cshtml │ │ │ ├── _CheckoutPreview.cshtml │ │ │ └── _MiniCart.cshtml │ │ └── Recommendation │ │ │ └── _Recommendation.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── _Layout.cshtml │ │ ├── _Spinner.cshtml │ │ ├── _ValidationScriptsPartial.cshtml │ │ ├── _breadcrumbs.cshtml │ │ └── _pagination.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml ├── Web.csproj ├── appsettings.Development.json ├── appsettings.json ├── build └── wwwroot │ ├── css │ └── site.css │ ├── favicon.ico │ ├── js │ ├── account.js │ ├── account.min.js │ ├── catalog.js │ ├── catalog.min.js │ ├── home.js │ ├── home.min.js │ └── site.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ ├── jquery │ ├── LICENSE.txt │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map │ └── vue │ ├── axios.min.js │ └── vue.min.js ├── docker-compose.debug.yml ├── docker-compose.yml ├── grafana └── provisioning │ ├── dashboards │ ├── dashboard.yml │ ├── docker_containers.json │ ├── docker_host.json │ ├── monitor_services.json │ └── nginx_container.json │ └── datasources │ └── datasource.yml └── prometheus └── prometheus.yml /.gitignore: -------------------------------------------------------------------------------- 1 | packages/ 2 | bin/ 3 | obj/ 4 | *.user 5 | *.suo 6 | 7 | .vs 8 | .vs/* 9 | 10 | nuget.config 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Bruno Hildenbrand 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/.dockerignore: -------------------------------------------------------------------------------- 1 | bin\ 2 | obj\ 3 | -------------------------------------------------------------------------------- /src/AccountSvc/AccountSvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/AccountSvc/Consumers/AccountInfoRequestConsumer.cs: -------------------------------------------------------------------------------- 1 | using AccountSvc.Services; 2 | using Microservices.Core.Contracts.Account; 3 | using MassTransit; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace AccountSvc.Consumers 10 | { 11 | /// 12 | /// AccountInfoRequestConsumer consumes to AccountInfoRequest events 13 | /// and responds asynchronously with account information. 14 | /// 15 | public class AccountInfoRequestConsumer 16 | : IConsumer 17 | { 18 | 19 | readonly IAccountSvc _svc; 20 | 21 | public AccountInfoRequestConsumer(IAccountSvc svc) 22 | { 23 | _svc = svc; 24 | } 25 | 26 | public async Task Consume(ConsumeContext context) 27 | { 28 | var acctId = context.Message.AccountId; 29 | if (acctId <= 0) 30 | { 31 | await context.RespondAsync(null); 32 | return; 33 | } 34 | 35 | var acct = await _svc.GetAccountById(acctId.ToString()); 36 | await context.RespondAsync(new AccountInfoResponse 37 | { 38 | AccountInfo = new AccountInfo 39 | { 40 | Id = acctId, 41 | Name = acct.Name, 42 | Email = acct.Email 43 | } 44 | }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AccountSvc/Consumers/NewsletterSubscribedConsumer.cs: -------------------------------------------------------------------------------- 1 | using AccountSvc.Services; 2 | using Microservices.Core.Contracts.Newsletter; 3 | using MassTransit; 4 | using System.Threading.Tasks; 5 | 6 | namespace AccountSvc.Consumers 7 | { 8 | public class NewsletterSubscribedConsumer : IConsumer 9 | { 10 | readonly IAccountSvc _svc; 11 | 12 | public NewsletterSubscribedConsumer(IAccountSvc svc) 13 | { 14 | _svc = svc; 15 | } 16 | 17 | public async Task Consume(ConsumeContext context) 18 | { 19 | await _svc.SubscribeToNewsletter(context.Message.Email); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/AccountSvc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "AccountSvc.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/AccountSvc/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | using System.Collections.Generic; 3 | 4 | namespace AccountSvc.Infrastructure.Options 5 | { 6 | public class AppConfig 7 | { 8 | public string ConnectionString { get; set; } 9 | public List EmailTemplates { get; set; } 10 | public MassTransitOptions MassTransit { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/AccountSvc/Models/Account.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AccountSvc.Models 6 | { 7 | public class Account 8 | { 9 | public string Id { get; set; } 10 | 11 | public string Name { get; set; } 12 | 13 | public string Email { get; set; } 14 | 15 | public bool SubscribedToNewsletter { get; set; } 16 | 17 | public string Password { get; set; } 18 | 19 | public string Salt { get; set; } 20 | 21 | public DateTime CreatedOn { get; set; } = DateTime.UtcNow; 22 | 23 | public DateTime LastUpdated { get; set; } = DateTime.UtcNow; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AccountSvc/Models/AccountHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace AccountSvc.Models 7 | { 8 | public class AccountHistory 9 | { 10 | public string Info { get; set; } 11 | 12 | public DateTime CreatedAt { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/AccountSvc/Models/Address.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace AccountSvc.Models 7 | { 8 | public class Address 9 | { 10 | public int Id { get; set; } 11 | 12 | public int AccountId { get; set; } 13 | 14 | public bool IsDefault { get; set; } 15 | 16 | public DateTime CreatedOn { get; set; } = DateTime.UtcNow; 17 | 18 | public DateTime LastUpdated { get; set; } = DateTime.UtcNow; 19 | 20 | [Required] 21 | [StringLength(100)] 22 | public string Name { get; set; } 23 | 24 | [Required] 25 | [StringLength(100)] 26 | public string Street { get; set; } 27 | 28 | [Required] 29 | [StringLength(10)] 30 | public string PostalCode { get; set; } 31 | 32 | [Required] 33 | [StringLength(50)] 34 | public string City { get; set; } 35 | 36 | [Required] 37 | [StringLength(2)] 38 | public string Region { get; set; } 39 | 40 | [Required] 41 | [StringLength(20)] 42 | public string Country { get; set; } 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/AccountSvc/Models/CreateAccount.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AccountSvc.Models 6 | { 7 | public class CreateAccount 8 | { 9 | public string Id { get; set; } 10 | 11 | public string Name { get; set; } 12 | 13 | public string Email { get; set; } 14 | 15 | public bool SubscribedToNewsletter { get; set; } 16 | 17 | public string Password { get; set; } 18 | 19 | public DateTime CreatedOn { get; set; } = DateTime.UtcNow; 20 | 21 | public DateTime LastUpdated { get; set; } = DateTime.UtcNow; 22 | 23 | public string Street { get; set; } 24 | 25 | public string PostalCode { get; set; } 26 | 27 | public string City { get; set; } 28 | 29 | public string Region { get; set; } 30 | 31 | public string Country { get; set; } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/AccountSvc/Models/PaymentInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace AccountSvc.Models 5 | { 6 | public enum PaymentMethod 7 | { 8 | MasterCard, 9 | Visa, 10 | Amex 11 | } 12 | 13 | public class PaymentInfo 14 | { 15 | public int Id { get; set; } 16 | 17 | public int AccountId { get; set; } 18 | 19 | public bool IsDefault { get; set; } 20 | 21 | [Required] 22 | public PaymentMethod Method { get; set; } 23 | 24 | [Required] 25 | [StringLength(100)] 26 | public string Name { get; set; } 27 | 28 | [Required] 29 | [DataType(DataType.CreditCard)] 30 | [StringLength(20)] 31 | public string Number { get; set; } 32 | 33 | [Required] 34 | [DataType(DataType.Date)] 35 | public DateTime ExpDate { get; set; } 36 | 37 | [Required] 38 | [Range(1, 999)] 39 | public int CVV { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/AccountSvc/Models/SignIn.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Extensions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Text; 6 | 7 | namespace Web.Models 8 | { 9 | public class SignIn 10 | { 11 | [Required] 12 | [EmailAddress] 13 | [Display(Name = "Email Address")] 14 | public string Email { get; set; } 15 | 16 | [Required] 17 | [DataType(DataType.Password)] 18 | public string Password { get; set; } 19 | 20 | public bool IsValid() => 21 | Email.HasValue(6) && Password.HasValue(3); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/AccountSvc/Models/UpdateAccount.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace AccountSvc.Models 4 | { 5 | public class UpdateAccount 6 | { 7 | public string Id { get; set; } 8 | 9 | [Required] 10 | [StringLength(100)] 11 | public string Name { get; set; } 12 | 13 | [Required] 14 | [EmailAddress] 15 | public string Email { get; set; } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/AccountSvc/Models/UpdatePassword.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace AccountSvc.Models 4 | { 5 | public class UpdatePassword 6 | { 7 | public string AccountId { get; set; } 8 | 9 | [Required] 10 | [StringLength(50)] 11 | [DataType(DataType.Password)] 12 | public string CurrentPassword { get; set; } 13 | 14 | [Required] 15 | [StringLength(50)] 16 | [DataType(DataType.Password)] 17 | public string NewPassword { get; set; } 18 | 19 | [Required] 20 | [StringLength(50)] 21 | [DataType(DataType.Password)] 22 | [Compare("NewPassword")] 23 | public string ConfirmPassword { get; set; } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AccountSvc/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AccountSvc 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/AccountSvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8004", 7 | "sslPort": 41404 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "AccountSvc": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/AccountSvc/Repositories/EventType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace AccountSvc.Repositories 7 | { 8 | /// 9 | /// Defines the events that can be registered on the event_type table 10 | /// 11 | public enum EventType 12 | { 13 | Login, 14 | AccountCreated, 15 | AccountUpdated, 16 | AccountClosed, 17 | PasswordCreated, 18 | PasswordUpdated, 19 | PasswordReset, 20 | ForgotPassword, 21 | AddressCreated, 22 | AddressUpdated, 23 | AddressRemoved, 24 | AddressSetDefault, 25 | PaymentInfoCreated, 26 | PaymentInfoUpdated, 27 | PaymentInfoRemoved, 28 | PaymentInfoSetDefault, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/AccountSvc/Repositories/IAccountRepository.cs: -------------------------------------------------------------------------------- 1 | using AccountSvc.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace AccountSvc.Repositories 6 | { 7 | public interface IAccountRepository 8 | { 9 | Task CreateAccount(CreateAccount account); 10 | Task UpdateAccount(UpdateAccount updAccount); 11 | Task UpdatePassword(UpdatePassword updPassword); 12 | Task GetAccountByEmail(string email); 13 | Task GetAccountById(string id); 14 | Task
GetAddressById(string addrId); 15 | Task AddAddress(Address addr); 16 | Task UpdateAddress(Address addr); 17 | Task RemoveAddress(string addressId); 18 | Task> GetAddressesByAccountId(string acctId); 19 | Task SetDefaultAddress(string acctId, int addressId); 20 | Task GetPaymentInfoById(string pmtId); 21 | Task AddPaymentInfo(PaymentInfo pmtInfo); 22 | Task UpdatePaymentInfo(PaymentInfo pmtInfo); 23 | Task RemovePaymentInfo(string pmtId); 24 | Task> GetPaymentInfosByAccountId(string accountId); 25 | Task SetDefaultPaymentInfo(string accountId, int pmtId); 26 | Task> GetAccountHistory(string acctId, int limit = 10); 27 | Task UpdateNewsletterSubscription(string email, bool subscribe = true); 28 | Task InsertLog( 29 | string accountId, 30 | EventType et, 31 | string requestedById = null, 32 | string refId = null, 33 | RefType? refType = null, 34 | string ip = null, 35 | string data = null); 36 | } 37 | } -------------------------------------------------------------------------------- /src/AccountSvc/Repositories/RefType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace AccountSvc.Repositories 7 | { 8 | public enum RefType 9 | { 10 | Address, 11 | PaymentInfo, 12 | ShippingInfo 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/AccountSvc/Services/IAccountSvc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using AccountSvc.Models; 4 | using Web.Models; 5 | 6 | namespace AccountSvc.Services 7 | { 8 | public interface IAccountSvc 9 | { 10 | Task CreateAccount(CreateAccount account); 11 | Task UpdateAccount(UpdateAccount updAccount); 12 | Task UpdatePassword(UpdatePassword updPassword); 13 | Task GetAccountById(string id); 14 | Task GetAccountByEmail(string email); 15 | Task
GetAddressById(string addrId); 16 | Task AddAddress(Address addr); 17 | Task UpdateAddress(Address addr); 18 | Task SubscribeToNewsletter(string email); 19 | Task RemoveAddress(string addressId); 20 | Task> GetAddressesByAccountId(string acctId); 21 | Task SetDefultAddress(string acctId, int addressId); 22 | Task GetPaymentInfoById(string pmtId); 23 | Task AddPaymentInfo(PaymentInfo pmtId); 24 | Task UpdatePaymentInfo(PaymentInfo pmtId); 25 | Task RemovePaymentInfo(string pmtId); 26 | Task> GetPaymentInfosByAccountId(string accountId); 27 | Task SetDefaultPaymentInfo(string accountId, int pmtId); 28 | Task> GetAccountHistory(string acctId); 29 | Task TrySignIn(SignIn signin); 30 | } 31 | } -------------------------------------------------------------------------------- /src/AccountSvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "MassTransit": { 10 | "Host": "rabbitmq://localhost", 11 | "Queue": "account" 12 | }, 13 | "ConnectionString": "Server=localhost;Port=3304;Database=accountdb;Uid=root;Pwd=todo" 14 | } 15 | -------------------------------------------------------------------------------- /src/AccountSvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "MassTransit": { 11 | "Host": "rabbitmq://rabbitmq", 12 | "Queue": "account" 13 | }, 14 | "ConnectionString": "Server=account-db;Port=3306;Database=accountdb;Uid=root;Pwd=todo", 15 | "EmailTemplates": [ 16 | { 17 | "TemplateName": "AccountCreated", 18 | "Subject": "[HildenCo] Welcome to ASP.NET Microservices!", 19 | "Body": "Hello {0},\nThanks for creating your account with us! We hope you have fun here!\n\nBest Regards,\nHildenCo." 20 | }, 21 | { 22 | "TemplateName": "AccountUpdated", 23 | "Subject": "[HildenCo] Account Updated", 24 | "Body": "Hello {0},\nWe noticed you updated your account. If this wasn't you please reach out to support.\n\nBest Regards,\nHildenCo." 25 | }, 26 | { 27 | "TemplateName": "PasswordUpdated", 28 | "Subject": "[HildenCo] Password Updated", 29 | "Body": "Hello {0},\nWe noticed your password was recently changed. If this wasn't you please reach out to support.\n\nBest Regards,\nHildenCo." 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/CatalogSvc/CatalogSvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/CatalogSvc/Consumers/ProductInfoRequestConsumer.cs: -------------------------------------------------------------------------------- 1 | using CatalogSvc.Infrastructure.Extensions; 2 | using CatalogSvc.Models; 3 | using CatalogSvc.Services; 4 | using Microservices.Core.Contracts.Catalog; 5 | using Microservices.Core.Infrastructure.Extensions; 6 | using MassTransit; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | 12 | namespace CatalogSvc.Consumers 13 | { 14 | public class ProductInfoRequestConsumer : IConsumer 15 | { 16 | 17 | readonly ICatalogSvc _svc; 18 | 19 | public ProductInfoRequestConsumer(ICatalogSvc svc) 20 | { 21 | _svc = svc; 22 | } 23 | 24 | public async Task Consume(ConsumeContext context) 25 | { 26 | if (!context.Message.Slugs.HasAny()) 27 | { 28 | await context.RespondAsync(null); 29 | return; 30 | } 31 | 32 | var pis = await _svc.GetProducts(context.Message.Slugs); 33 | var productInfoResponse = new ProductInfoResponse 34 | { 35 | ProductInfos = ((List)pis).ConvertAll(pi => pi.ToProductInfo()) 36 | }; 37 | 38 | await context.RespondAsync(productInfoResponse); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/CatalogSvc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "CatalogSvc.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/CatalogSvc/Infrastructure/Db/IMongoClient.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace CatalogSvc.Infrastructure.Db 5 | { 6 | public interface IMongoClient 7 | { 8 | string Collection { get; set; } 9 | Task Insert(T item); 10 | Task> GetAll(); 11 | Task> Find(string column, string value); 12 | Task> Find(string column, List values); 13 | } 14 | } -------------------------------------------------------------------------------- /src/CatalogSvc/Infrastructure/Extensions/ProductExtensions.cs: -------------------------------------------------------------------------------- 1 | using CatalogSvc.Models; 2 | using Microservices.Core.Contracts.Catalog; 3 | 4 | namespace CatalogSvc.Infrastructure.Extensions 5 | { 6 | public static class ProductExtensions 7 | { 8 | public static ProductInfo ToProductInfo(this Product product) 9 | { 10 | if (product == null) 11 | return null; 12 | 13 | return new ProductInfo 14 | { 15 | Slug = product.Slug, 16 | Name = product.Name, 17 | Description = product.Description, 18 | Price = product.Price 19 | }; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CatalogSvc/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | 3 | namespace CatalogSvc.Infrastructure.Options 4 | { 5 | public class AppConfig 6 | { 7 | public MassTransitOptions MassTransit { get; set; } 8 | public MongoOptions Mongo { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/CatalogSvc/Models/Category.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace CatalogSvc.Models 8 | { 9 | public class Category 10 | { 11 | [BsonId] 12 | [BsonRepresentation(BsonType.ObjectId)] 13 | public string Id { get; set; } 14 | public string Slug { get; set; } 15 | public string Name { get; set; } 16 | public string Description { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CatalogSvc/Models/Product.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace CatalogSvc.Models 8 | { 9 | public class Product 10 | { 11 | [BsonId] 12 | [BsonRepresentation(BsonType.ObjectId)] 13 | public string Id { get; set; } 14 | public string Slug { get; set; } 15 | public string Name { get; set; } 16 | public string Description { get; set; } 17 | public decimal Price { get; set; } 18 | public string Currency { get; set; } 19 | public string CategoryId { get; set; } 20 | public string CategoryName { get; set; } 21 | public int Rating { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/CatalogSvc/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace CatalogSvc 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/CatalogSvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8001", 7 | "sslPort": 41401 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "CatalogSvc": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/CatalogSvc/Repositories/CatalogRepository.cs: -------------------------------------------------------------------------------- 1 | using CatalogSvc.Infrastructure.Db; 2 | using CatalogSvc.Models; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace CatalogSvc.Repositories 8 | { 9 | public class CatalogRepository : ICatalogRepository 10 | { 11 | readonly IMongoClient db; 12 | 13 | public CatalogRepository(IMongoClient db) 14 | { 15 | this.db = db; 16 | } 17 | 18 | public async Task> GetCategories() 19 | { 20 | db.Collection = "categories"; 21 | return await db.GetAll(); 22 | } 23 | 24 | public async Task GetCategory(string slug) 25 | { 26 | db.Collection = "categories"; 27 | return (await db.Find("Slug", slug)).SingleOrDefault(); 28 | } 29 | 30 | public async Task GetProduct(string slug) 31 | { 32 | db.Collection = "products"; 33 | return (await db.Find("Slug", slug)).SingleOrDefault(); 34 | } 35 | 36 | public async Task> GetProducts(List slugs) 37 | { 38 | db.Collection = "products"; 39 | return await db.Find("Slug", slugs); 40 | } 41 | 42 | public async Task> GetProductsByCategory(string slug) 43 | { 44 | db.Collection = "products"; 45 | return await db.Find("CategoryId", slug); 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/CatalogSvc/Repositories/ICatalogRepository.cs: -------------------------------------------------------------------------------- 1 | using CatalogSvc.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace CatalogSvc.Repositories 6 | { 7 | public interface ICatalogRepository 8 | { 9 | Task> GetCategories(); 10 | Task GetCategory(string slug); 11 | Task GetProduct(string slug); 12 | Task> GetProductsByCategory(string slug); 13 | Task> GetProducts(List slugs); 14 | } 15 | } -------------------------------------------------------------------------------- /src/CatalogSvc/Services/CatalogSvc.cs: -------------------------------------------------------------------------------- 1 | using CatalogSvc.Models; 2 | using CatalogSvc.Repositories; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace CatalogSvc.Services 7 | { 8 | public class CatalogSvc : ICatalogSvc 9 | { 10 | readonly ICatalogRepository _repo; 11 | 12 | public CatalogSvc(ICatalogRepository repo) 13 | { 14 | _repo = repo; 15 | } 16 | 17 | public async Task> GetCategories() 18 | { 19 | return await _repo.GetCategories(); 20 | } 21 | 22 | public async Task GetCategory(string slug) 23 | { 24 | return await _repo.GetCategory(slug); 25 | } 26 | 27 | public async Task GetProduct(string slug) 28 | { 29 | return await _repo.GetProduct(slug); 30 | } 31 | 32 | public async Task> GetProducts(List slugs) 33 | { 34 | return await _repo.GetProducts(slugs); 35 | } 36 | 37 | public async Task> GetProductsByCategory(string slug) 38 | { 39 | return await _repo.GetProductsByCategory(slug); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/CatalogSvc/Services/ICatalogSvc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using CatalogSvc.Models; 4 | 5 | namespace CatalogSvc.Services 6 | { 7 | public interface ICatalogSvc 8 | { 9 | Task> GetCategories(); 10 | Task GetCategory(string slug); 11 | Task> GetProductsByCategory(string slug); 12 | Task GetProduct(string slug); 13 | Task> GetProducts(List slugs); 14 | } 15 | } -------------------------------------------------------------------------------- /src/CatalogSvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "Mongo": { 10 | "ConnectionString": "mongodb://localhost:3301", 11 | "Db": "catalog", 12 | "Collection": "products" 13 | }, 14 | "MassTransit": { 15 | "Host": "rabbitmq://localhost", 16 | "Queue": "catalog" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CatalogSvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "Mongo": { 11 | "ConnectionString": "mongodb://catalog-db:27017", 12 | "Db": "catalog", 13 | "Collection": "products" 14 | }, 15 | "MassTransit": { 16 | "Host": "rabbitmq://rabbitmq", 17 | "Queue": "catalog" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/CatalogSvc/categories.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Slug": "sports", 4 | "Name": "Sports" 5 | }, 6 | { 7 | "Slug": "games", 8 | "Name": "Games" 9 | }, 10 | { 11 | "Slug": "books", 12 | "Name": "Books" 13 | }, 14 | { 15 | "Slug": "computers", 16 | "Name": "Computers" 17 | }, 18 | { 19 | "Slug": "tvs", 20 | "Name": "TVs" 21 | }, 22 | { 23 | "Slug": "home", 24 | "Name": "Home" 25 | }, 26 | { 27 | "Slug": "musical-instruments", 28 | "Name": "Musical Instruments" 29 | }, 30 | { 31 | "Slug": "headphones-audio", 32 | "Name": "Headphones & Audio" 33 | }, 34 | { 35 | "Slug": "phones", 36 | "Name": "Phones" 37 | } 38 | ] -------------------------------------------------------------------------------- /src/CatalogSvc/products.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd9/aspnet-microservices/fb64d38bc154acb19bed7dc5b29292192c4511f2/src/CatalogSvc/products.json -------------------------------------------------------------------------------- /src/Microservices.Core.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microservices.Core", "Microservices.Core\Microservices.Core.csproj", "{E8F229B7-4EBB-4E6E-8260-164B669B018B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {E8F229B7-4EBB-4E6E-8260-164B669B018B}.Debug|Any CPU.ActiveCfg = Release|Any CPU 15 | {E8F229B7-4EBB-4E6E-8260-164B669B018B}.Debug|Any CPU.Build.0 = Release|Any CPU 16 | {E8F229B7-4EBB-4E6E-8260-164B669B018B}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {E8F229B7-4EBB-4E6E-8260-164B669B018B}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {CC4644C0-EA84-44CF-86F8-780A4FE18D65} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Account/AccountCreated.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Base; 2 | 3 | namespace Microservices.Core.Contracts.Account 4 | { 5 | public class AccountCreated : EventBase 6 | { 7 | public string Name { get; set; } 8 | 9 | public string Email { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Account/AccountInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Microservices.Core.Contracts.Account 2 | { 3 | public class AccountInfo 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Email { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Account/AccountInfoRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Contracts.Account 6 | { 7 | public class AccountInfoRequest 8 | { 9 | public int AccountId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Account/AccountInfoResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Contracts.Account 6 | { 7 | public class AccountInfoResponse 8 | { 9 | public AccountInfo AccountInfo { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Base/CommandBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Contracts.Base 6 | { 7 | public abstract class CommandBase 8 | { 9 | public string RequestId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Base/EventBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Contracts.Base 6 | { 7 | public abstract class EventBase : CommandBase 8 | { 9 | public DateTime CreatedOn => DateTime.UtcNow; 10 | public string SubmittedBy { get; set; } 11 | public string IP { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Catalog/ProductInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Microservices.Core.Contracts.Catalog 2 | { 3 | public class ProductInfo 4 | { 5 | public string Name { get; set; } 6 | public string Slug { get; set; } 7 | public string Description { get; set; } 8 | public decimal Price { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Catalog/ProductInfoRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Contracts.Catalog 6 | { 7 | public class ProductInfoRequest 8 | { 9 | public List Slugs { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Catalog/ProductInfoResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Contracts.Catalog 6 | { 7 | public class ProductInfoResponse 8 | { 9 | public List ProductInfos { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Newsletter/NewsletterSubscribed.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Base; 2 | 3 | namespace Microservices.Core.Contracts.Newsletter 4 | { 5 | public class NewsletterSubscribed : EventBase 6 | { 7 | public string Name { get; set; } 8 | 9 | public string Email { get; set; } 10 | 11 | public override string ToString() => 12 | $"[NewsletterSubscription] \"{Name}\" <{Email}>"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Notification/SendMail.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Contracts.Notification 6 | { 7 | public class SendMail 8 | { 9 | public string ToName { get; set; } 10 | public string ToEmail { get; set; } 11 | public string Subject { get; set; } 12 | public string Body { get; set; } 13 | public bool IsHtml { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Orders/OrderSubmitted.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Base; 2 | using System.Collections.Generic; 3 | 4 | namespace Microservices.Core.Contracts.Orders 5 | { 6 | public class OrderSubmitted : EventBase 7 | { 8 | public List Slugs { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Payment/PaymentRequest.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Base; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Microservices.Core.Contracts.Payment 7 | { 8 | public class PaymentRequest : CommandBase 9 | { 10 | public int AccountId { get; set; } 11 | 12 | public int OrderId { get; set; } 13 | 14 | public string Currency { get; set; } 15 | 16 | public string Number { get; set; } 17 | 18 | public decimal Amount { get; set; } 19 | 20 | /// 21 | /// Payment Method: MasterCard, Visa, Amex, etc 22 | /// 23 | public string Method { get; set; } 24 | 25 | public string Name { get; set; } 26 | 27 | public DateTime ExpDate { get; set; } 28 | 29 | public int CVV { get; set; } 30 | 31 | /// 32 | /// A fake result just to simulate the response from the payment gateway 33 | /// 34 | public PaymentStatus FakeStatus { get; set; } 35 | 36 | /// 37 | /// A Fake delay in ms to simulate a delay to the payment gateway 38 | /// 39 | public int FakeDelay { get; set; } = 5000; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Payment/PaymentResponse.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Base; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Microservices.Core.Contracts.Payment 7 | { 8 | public class PaymentResponse : CommandBase 9 | { 10 | public int AccountId { get; set; } 11 | 12 | public int OrderId { get; set; } 13 | 14 | public PaymentStatus Status { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Payment/PaymentStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Microservices.Core.Contracts.Payment 2 | { 3 | public enum PaymentStatus 4 | { 5 | Pending, 6 | Authorized, 7 | Declined, 8 | Cancelled, 9 | Refunded 10 | } 11 | } -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Shipping/ShippingProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Microservices.Core.Contracts.Shipping 2 | { 3 | public enum ShippingProvider 4 | { 5 | Fedex, 6 | UPS, 7 | DHL, 8 | CanadaPost 9 | } 10 | } -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Shipping/ShippingRequest.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Base; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Microservices.Core.Contracts.Shipping 7 | { 8 | public class ShippingRequest : CommandBase 9 | { 10 | public int AccountId { get; set; } 11 | 12 | public int OrderId { get; set; } 13 | 14 | public string Name { get; set; } 15 | 16 | public decimal Amount { get; set; } 17 | 18 | public string Currency { get; set; } 19 | 20 | public string Street { get; set; } 21 | 22 | public string PostalCode { get; set; } 23 | 24 | public string City { get; set; } 25 | 26 | public string Region { get; set; } 27 | 28 | public string Country { get; set; } 29 | 30 | public ShippingStatus Status { get; set; } 31 | 32 | public ShippingProvider Provider { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Shipping/ShippingResponse.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Base; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Microservices.Core.Contracts.Shipping 7 | { 8 | public class ShippingResponse : CommandBase 9 | { 10 | public int AccountId { get; set; } 11 | 12 | public int OrderId { get; set; } 13 | 14 | public string Number { get; set; } 15 | 16 | public ShippingStatus Status { get; set; } 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Microservices.Core/Contracts/Shipping/ShippingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Microservices.Core.Contracts.Shipping 2 | { 3 | public enum ShippingStatus 4 | { 5 | Pending, 6 | Delivered, 7 | Declined, 8 | Cancelled, 9 | Invalid, 10 | Error 11 | } 12 | } -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Crypt/Crypt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | namespace Microservices.Core.Infrastructure.Crypt 7 | { 8 | public class Crypt 9 | { 10 | public static string HashPassword(string passwd, string salt) 11 | { 12 | using (var sha = SHA256.Create()) 13 | { 14 | var hash = sha.ComputeHash(Encoding.Unicode.GetBytes(passwd + salt)); 15 | return Convert.ToBase64String(hash); 16 | } 17 | } 18 | 19 | public static string GenerateSalt() 20 | { 21 | return Guid.NewGuid().ToString().Replace("-", ""); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Extensions/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Infrastructure.Extensions 6 | { 7 | public static class DateTimeExtensions 8 | { 9 | public static string ToExpDate(this DateTime date) 10 | { 11 | return date == null ? 12 | null : 13 | date.ToString("MM/yy"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microservices.Core.Infrastructure.Extensions 4 | { 5 | public static class EnumExtensions 6 | { 7 | public static TEnum Parse(this Enum val) 8 | { 9 | return (TEnum)Enum.Parse( 10 | typeof(TEnum), 11 | val.ToString(), 12 | true); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Extensions/ExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Infrastructure.Extensions 6 | { 7 | public static class ExceptionExtensions 8 | { 9 | 10 | /// 11 | /// A one-liner way to throw expressions. 12 | /// Read: https://blog.hildenco.com/2017/08/improving-code-readability-with-generic.html 13 | /// 14 | /// 15 | public class Throw 16 | where T : Exception 17 | { 18 | public static void If(bool isInvalid, string message = null) 19 | { 20 | if (isInvalid) 21 | { 22 | var e = Activator.CreateInstance(typeof(T), message) as Exception; 23 | if (e != null) throw e; 24 | 25 | throw Activator.CreateInstance(); 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Extensions/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Microservices.Core.Infrastructure.Extensions 7 | { 8 | public static class ListExtensions 9 | { 10 | public static void AddIfDoesNotExist(this List list, T item) 11 | { 12 | if (!list.Contains(item)) 13 | { 14 | list.Add(item); 15 | } 16 | } 17 | 18 | public static bool HasAny(this List list) 19 | { 20 | return list != null && list.Any(); 21 | } 22 | 23 | public static bool HasAny(this Dictionary dict) 24 | { 25 | return dict != null && dict.Any(); 26 | } 27 | 28 | public static List Intersect(this List> listOfLists) 29 | { 30 | var ret = new List(); 31 | var init = false; 32 | 33 | foreach (var accessList in listOfLists) 34 | { 35 | if (!init) 36 | { 37 | ret.AddRange(accessList); 38 | init = true; 39 | continue; 40 | } 41 | 42 | ret.RemoveAll(i => !accessList.Exists(access => access.Equals(i))); 43 | } 44 | 45 | return ret; 46 | } 47 | 48 | public static string ToStr(this Dictionary d) 49 | { 50 | if (!HasAny(d)) return ""; 51 | 52 | return string.Join("\n", d.Select(x => x.Key + ": " + x.Value)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Infrastructure.Extensions 6 | { 7 | public static class StringExtensions 8 | { 9 | public static bool HasValue(this string val, int minLength = 0) 10 | { 11 | if (val == null) 12 | return false; 13 | 14 | return minLength == 0 ? 15 | !string.IsNullOrEmpty(val) : 16 | (val ?? "").Length >= minLength; 17 | } 18 | 19 | public static string FormatWith(this string val, params object[] args) 20 | { 21 | if (val == null) 22 | return null; 23 | 24 | return string.Format(val, args); 25 | } 26 | 27 | public static string Append(this string val, params object[] args) 28 | { 29 | if (val == null) 30 | return string.Join("", args); 31 | 32 | return val + string.Join("", args); 33 | } 34 | 35 | public static string MaskCC(this string number) 36 | { 37 | return number.HasValue(4) ? 38 | $"{number.Substring(0, 2)}-XXXX-XXXX-{number.Substring(number.Length - 2, 2)}" : 39 | null; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Global.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Infrastructure 6 | { 7 | public static class Global 8 | { 9 | public static class Endpoints 10 | { 11 | public static string ProductInfo = "product-info"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Options/EmailTemplate.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Microservices.Core.Infrastructure.Options 7 | { 8 | public class EmailTemplate 9 | { 10 | /// 11 | /// The name of the template (optional). 12 | /// Use only when more than one template is set in the config file. 13 | /// 14 | public string TemplateName { get; set; } 15 | public string Subject { get; set; } 16 | public string Body { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Options/MassTransitOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Infrastructure.Options 6 | { 7 | public class MassTransitOptions 8 | { 9 | public string Host { get; set; } 10 | public string Queue { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Options/MongoOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Infrastructure.Options 6 | { 7 | public class MongoOptions 8 | { 9 | public string ConnectionString { get; set; } 10 | public string Db { get; set; } 11 | public string Collection { get; set; } 12 | 13 | /// 14 | /// Simplify logging 15 | /// 16 | /// 17 | public override string ToString() => 18 | $"ConnStr: {ConnectionString}, Db: {Db}, Collection: {Collection}"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Options/RedisOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Infrastructure.Options 6 | { 7 | public class RedisOptions 8 | { 9 | public string Configuration { get; set; } 10 | public string InstanceName { get; set; } 11 | 12 | /// 13 | /// To simplify logging 14 | /// 15 | /// 16 | public override string ToString() => 17 | $"Configuration: {Configuration}, InstanceName: {InstanceName}"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Microservices.Core/Infrastructure/Options/SmtpOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Microservices.Core.Infrastructure.Options 6 | { 7 | public class SmtpOptions 8 | { 9 | public string Host { get; set; } 10 | public int Port { get; set; } 11 | public string Username { get; set; } 12 | public string Password { get; set; } 13 | public string FromName { get; set; } 14 | public string FromEmail { get; set; } 15 | 16 | /// 17 | /// If set, overrides the original email. 18 | /// Use for testing purposes. 19 | /// Please, don't spam others! 20 | /// 21 | public string EmailOverride { get; set; } 22 | 23 | public override string ToString() => 24 | $"Host: {Host}:{Port}, Username: {Username}, Password: {Password}, From: \"{FromName} <{FromEmail}>\""; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Microservices.Core/Microservices.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | true 6 | Microservices 7 | Microservices 8 | 0.0.2 9 | https://github.com/hd9/aspnet-microservices 10 | https://github.com/hd9/aspnet-microservices 11 | 12 | MIT 13 | Demo package needed to build the AspNetMicroservices project available at: https://github.com/hd9/aspnet-microservices. 14 | 15 | For more information visit https://blog.hildenco.com 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Microservices.Core/README.md: -------------------------------------------------------------------------------- 1 | # Microservices.Core 2 | Shared packages between AspNetMicroservices. 3 | 4 | Currently published on GitHub at: 5 | [github.com/hd9/aspnet-microservices](https://github.com/hd9/aspnet-microservices). 6 | 7 | ## Building your packages 8 | To build your package, make sure you add a new [Personal Access Token](https://github.com/settings/tokens) 9 | and set it as an environment variable as: `GITHUB_API_KEY=`. 10 | Both `docker build` and `docker-compose build` rely on that environment 11 | variable to be present to build the images. 12 | 13 | Also, don't forget to add your username on all `nuget.config` files. 14 | 15 | ## Pushing our packages 16 | To push this package to your repo do: 17 | 1. Adjust your push settings on GitHub at 18 | [github.com/settings/tokens](https://github.com/settings/tokens) 19 | and check `write:packages` 20 | 2. build your package with `dotnet pack` 21 | 3. push to your GitHub pkg repository. 22 | 23 | 24 | For more information, check 25 | [Configuring dotnet CLI for use with GitHub Packages](https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-dotnet-cli-for-use-with-github-packages). 26 | -------------------------------------------------------------------------------- /src/NewsletterSvc/Controllers/NewsletterController.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.Extensions.Configuration; 7 | using NewsletterSvc.Models; 8 | using NewsletterSvc.Services; 9 | 10 | namespace NewsletterSvc.Controllers 11 | { 12 | [Route("[controller]/[action]")] 13 | [ApiController] 14 | public class NewsletterController : ControllerBase 15 | { 16 | 17 | readonly INewsletterSvc _svc; 18 | 19 | public NewsletterController(INewsletterSvc svc) 20 | { 21 | _svc = svc; 22 | } 23 | 24 | [Route("/help")] 25 | public IActionResult Help() 26 | { 27 | return Ok("The Newsletter service is alive! Try GET /api/v1/signups"); 28 | } 29 | 30 | [Route("/ping")] 31 | public IActionResult Ping() 32 | { 33 | return Ok(); 34 | } 35 | 36 | /// 37 | /// Creates a new signup request 38 | /// 39 | /// 40 | /// 41 | [Route("/api/v1/signup")] 42 | [HttpPost] 43 | public async Task Signup(Signup s) 44 | { 45 | if (s == null) 46 | return BadRequest(); 47 | 48 | await _svc.RegistrerSignup(s); 49 | 50 | return Ok(); 51 | } 52 | 53 | [Route("/api/v1/signups")] 54 | public async Task GetSignups() 55 | { 56 | var signups = await _svc.GetSignups(); 57 | return Ok(signups); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/NewsletterSvc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "NewsletterSvc.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/NewsletterSvc/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | 3 | namespace NewsletterSvc.Infrastructure.Options 4 | { 5 | public class AppConfig 6 | { 7 | public string ConnectionString { get; set; } 8 | public MassTransitOptions MassTransit { get; set; } 9 | public EmailTemplate EmailTemplate { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/NewsletterSvc/Models/Signup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace NewsletterSvc.Models 6 | { 7 | public class Signup 8 | { 9 | public string Id { get; set; } 10 | public string Name { get; set; } 11 | public string Email { get; set; } 12 | public DateTime CreatedOn { get; set; } = DateTime.UtcNow; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/NewsletterSvc/NewsletterSvc.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/NewsletterSvc/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace NewsletterSvc 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/NewsletterSvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8002", 7 | "sslPort": 41402 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "NewsletterSvc": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/NewsletterSvc/Repositories/INewsletterRepository.cs: -------------------------------------------------------------------------------- 1 | using NewsletterSvc.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace NewsletterSvc.Repositories 6 | { 7 | public interface INewsletterRepository 8 | { 9 | Task Insert(Signup s); 10 | Task> GetSignups(int recs); 11 | } 12 | } -------------------------------------------------------------------------------- /src/NewsletterSvc/Repositories/NewsletterRepository.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using MySql.Data.MySqlClient; 3 | using NewsletterSvc.Models; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace NewsletterSvc.Repositories 9 | { 10 | public class NewsletterRepository : INewsletterRepository 11 | { 12 | readonly string _connStr; 13 | readonly string insSignup = "INSERT INTO newsletter (name, email, created_at) values (@name, @email, sysdate())"; 14 | readonly string selSignup = "SELECT * FROM newsletter ORDER BY created_at DESC limit @limit"; 15 | 16 | public NewsletterRepository(string connStr) 17 | { 18 | DefaultTypeMap.MatchNamesWithUnderscores = true; 19 | _connStr = connStr; 20 | } 21 | 22 | public async Task Insert(Signup s) 23 | { 24 | if (s == null) 25 | return; 26 | 27 | using (var conn = new MySqlConnection(_connStr)) 28 | { 29 | var cnt = await conn.ExecuteAsync(insSignup, new { s.Name, s.Email }); 30 | } 31 | } 32 | 33 | public async Task> GetSignups(int recs) 34 | { 35 | using (var conn = new MySqlConnection(_connStr)) 36 | { 37 | return (await conn.QueryAsync( 38 | selSignup, 39 | new { limit = recs } 40 | )).ToList(); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/NewsletterSvc/Services/INewsletterSvc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using NewsletterSvc.Models; 4 | 5 | namespace NewsletterSvc.Services 6 | { 7 | public interface INewsletterSvc 8 | { 9 | Task RegistrerSignup(Signup s); 10 | Task> GetSignups(int recs = 10); 11 | } 12 | } -------------------------------------------------------------------------------- /src/NewsletterSvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "MassTransit": { 10 | "Host": "rabbitmq://localhost", 11 | "Queue": "newsletter" 12 | }, 13 | "ConnectionString": "Server=localhost;Port=3302;Database=newsletterdb;Uid=root;Pwd=todo" 14 | } 15 | -------------------------------------------------------------------------------- /src/NewsletterSvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "MassTransit": { 11 | "Host": "rabbitmq://rabbitmq", 12 | "Queue": "newsletter" 13 | }, 14 | "ConnectionString": "Server=newsletter-db;Port=3306;Database=newsletterdb;Uid=root;Pwd=todo", 15 | "EmailTemplate": { 16 | "TemplateName": "NewsletterSubscribed", 17 | "Subject": "[HildenCo] Welcome to our newsletter", 18 | "Body": "Hello {0},\nthanks for subscribing to our newsletter!\n\nBest Regards,\nHildenCo." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/NewsletterSvc/db.sql: -------------------------------------------------------------------------------- 1 | -- NewsletterSvc database init script 2 | create database if not exists newsletterdb; 3 | use newsletterdb; 4 | 5 | CREATE TABLE if not exists newsletter ( 6 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 7 | name VARCHAR(1000) NOT NULL, 8 | email VARCHAR(300) NOT NULL, 9 | created_at DATETIME NOT NULL 10 | ); -------------------------------------------------------------------------------- /src/NotificationSvc/Controllers/NotificationController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microservices.Core.Infrastructure.Options; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.Extensions.Configuration; 8 | using NotificationSvc.Models; 9 | using NotificationSvc.Services; 10 | 11 | namespace NotificationSvc.Controllers 12 | { 13 | [Route("[controller]/[action]")] 14 | [ApiController] 15 | public class NotificationController : ControllerBase 16 | { 17 | 18 | readonly INotificationSvc _svc; 19 | 20 | public NotificationController(INotificationSvc svc) 21 | { 22 | _svc = svc; 23 | } 24 | 25 | [Route("/ping")] 26 | public IActionResult Ping() 27 | { 28 | return Ok(); 29 | } 30 | 31 | [Route("/help")] 32 | public IActionResult Help() 33 | { 34 | var instruction = $@"The Notification service is alive! Try GET /api/v1/notifications"; 35 | return Ok(instruction); 36 | } 37 | 38 | [Route("/api/v1/notifications")] 39 | public async Task GetNotifications() 40 | { 41 | var notifs = await _svc.GetNotifications(); 42 | return Ok(notifs); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/NotificationSvc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "NotificationSvc.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/NotificationSvc/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | 3 | namespace NotificationSvc.Infrastructure.Options 4 | { 5 | public class AppConfig 6 | { 7 | public MassTransitOptions MassTransit { get; set; } 8 | public string ConnectionString { get; set; } 9 | public SmtpOptions SmtpOptions { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/NotificationSvc/Models/Notification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace NotificationSvc.Models 6 | { 7 | public class Notification 8 | { 9 | public long Id { get; set; } 10 | public string Name { get; set; } 11 | public string Email { get; set; } 12 | public DateTime CreatedAt { get; set; } = DateTime.UtcNow; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/NotificationSvc/NotificationSvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/NotificationSvc/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace NotificationSvc 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/NotificationSvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8006", 7 | "sslPort": 41406 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "NotificationSvc": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/NotificationSvc/Repositories/INotificationRepository.cs: -------------------------------------------------------------------------------- 1 | using NotificationSvc.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace NotificationSvc.Repositories 6 | { 7 | public interface INotificationRepository 8 | { 9 | Task Insert(string name, string email, char type); 10 | Task> GetNotifications(int recs); 11 | } 12 | } -------------------------------------------------------------------------------- /src/NotificationSvc/Repositories/NotificationRepository.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using MySql.Data.MySqlClient; 3 | using NotificationSvc.Models; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace NotificationSvc.Repositories 9 | { 10 | public class NotificationRepository : INotificationRepository 11 | { 12 | 13 | readonly string _connStr; 14 | readonly string insNotification = "INSERT INTO notification (name, email, created_at, type) values (@name, @email, sysdate(), @type)"; 15 | readonly string selNotification = "SELECT * FROM notification ORDER BY created_at DESC limit @limit"; 16 | 17 | public NotificationRepository(string connStr) 18 | { 19 | DefaultTypeMap.MatchNamesWithUnderscores = true; 20 | _connStr = connStr; 21 | } 22 | 23 | public async Task> GetNotifications(int recs) 24 | { 25 | using (var conn = new MySqlConnection(_connStr)) 26 | { 27 | return (await conn.QueryAsync( 28 | selNotification, 29 | new { limit = recs } 30 | )).ToList(); 31 | } 32 | } 33 | 34 | public async Task Insert(string name, string email, char type) 35 | { 36 | using (var conn = new MySqlConnection(_connStr)) 37 | { 38 | var cnt = await conn.ExecuteAsync(insNotification, new { name, email, type }); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/NotificationSvc/Services/INotificationSvc.cs: -------------------------------------------------------------------------------- 1 | using NotificationSvc.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace NotificationSvc.Services 6 | { 7 | public interface INotificationSvc 8 | { 9 | Task LogNotification(string name, string email, char type); 10 | Task> GetNotifications(int recs = 10); 11 | } 12 | } -------------------------------------------------------------------------------- /src/NotificationSvc/Services/NotificationSvc.cs: -------------------------------------------------------------------------------- 1 | using NotificationSvc.Models; 2 | using NotificationSvc.Repositories; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace NotificationSvc.Services 7 | { 8 | public class NotificationSvc : INotificationSvc 9 | { 10 | private INotificationRepository _repo; 11 | 12 | public NotificationSvc(INotificationRepository repo) 13 | { 14 | _repo = repo; 15 | } 16 | 17 | public async Task> GetNotifications(int recs = 10) 18 | { 19 | return await _repo.GetNotifications(recs); 20 | } 21 | 22 | public async Task LogNotification(string name, string email, char type) 23 | { 24 | await _repo.Insert(name, email, type); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/NotificationSvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "MassTransit": { 10 | "Host": "rabbitmq://localhost", 11 | "Queue": "notification" 12 | }, 13 | "ConnectionString": "Server=localhost;Port=3306;Database=notificationdb;Uid=root;Pwd=todo" 14 | } 15 | -------------------------------------------------------------------------------- /src/NotificationSvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "MassTransit": { 11 | "Host": "rabbitmq://rabbitmq", 12 | "Queue": "notification" 13 | }, 14 | "ConnectionString": "Server=notification-db;Port=3306;Database=notificationdb;Uid=root;Pwd=todo", 15 | "SmtpOptions": { 16 | "Host": "", 17 | "Port": "587", 18 | "Username": "", 19 | "Password": "", 20 | "FromName": "HildenCo Web Store", 21 | "FromEmail": "webstore@hildenco.com", 22 | "EmailOverride": "" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/NotificationSvc/db.sql: -------------------------------------------------------------------------------- 1 | -- NotificationSvc database init script 2 | create database if not exists notificationdb; 3 | use notificationdb; 4 | 5 | CREATE TABLE if not exists notification ( 6 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 7 | name VARCHAR(1000) NOT NULL, 8 | email VARCHAR(300) NOT NULL, 9 | created_at DATETIME NOT NULL, 10 | type char(1) NOT NULL 11 | ); -------------------------------------------------------------------------------- /src/OrderSvc/Consumers/PaymentResponseConsumer.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Payment; 2 | using MassTransit; 3 | using OrderSvc.Services; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace OrderSvc.Consumers 8 | { 9 | /// 10 | /// Consumes a payment response, a response from the payment service 11 | /// regarding a payment requested previously. 12 | /// 13 | public class PaymentResponseConsumer 14 | : IConsumer 15 | { 16 | 17 | readonly IOrderSvc _svc; 18 | 19 | public PaymentResponseConsumer(IOrderSvc svc) 20 | { 21 | _svc = svc; 22 | } 23 | 24 | public async Task Consume(ConsumeContext context) 25 | { 26 | await _svc.OnPaymentProcessed(context.Message); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/OrderSvc/Consumers/ShippingResponseConsumer.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Payment; 2 | using Microservices.Core.Contracts.Shipping; 3 | using MassTransit; 4 | using OrderSvc.Services; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace OrderSvc.Consumers 9 | { 10 | /// 11 | /// ShippingResponseConsumer consumes a shipping response 12 | /// and triggers the workflow so offers can be processed. 13 | /// 14 | public class ShippingResponseConsumer 15 | : IConsumer 16 | { 17 | 18 | readonly IOrderSvc _svc; 19 | 20 | public ShippingResponseConsumer(IOrderSvc svc) 21 | { 22 | _svc = svc; 23 | } 24 | 25 | public async Task Consume(ConsumeContext context) 26 | { 27 | await _svc.OnShippingProcessed(context.Message); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/OrderSvc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "OrderSvc.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/OrderSvc/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | using System.Collections.Generic; 3 | 4 | namespace OrderSvc.Infrastructure.Options 5 | { 6 | public class AppConfig 7 | { 8 | public MassTransitOptions MassTransit { get; set; } 9 | public List EmailTemplates { get; set; } 10 | public string ConnectionString { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/OrderSvc/Models/LineItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace OrderSvc.Models 7 | { 8 | public class LineItem 9 | { 10 | public string Id { get; set; } 11 | 12 | [Required] 13 | public string Name { get; set; } 14 | 15 | [Required] 16 | public string Slug { get; set; } 17 | 18 | [Range(0,100000)] 19 | [Required] 20 | public decimal Price { get; set; } 21 | 22 | [Range(1, 999)] 23 | [Required] 24 | public int Qty { get; set; } = 1; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/OrderSvc/Models/OrderStatus.cs: -------------------------------------------------------------------------------- 1 | namespace OrderSvc.Models 2 | { 3 | public enum OrderStatus 4 | { 5 | New, 6 | Submitted, 7 | PaymentApproved, 8 | PaymentDeclined, 9 | WaitingShipping, 10 | Shipped, 11 | Complete, 12 | Cancelled 13 | } 14 | } -------------------------------------------------------------------------------- /src/OrderSvc/Models/PaymentInfo.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Extensions; 2 | using System; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace OrderSvc.Models 6 | { 7 | public class PaymentInfo 8 | { 9 | public int Id { get; set; } 10 | 11 | public int AccountId { get; set; } 12 | 13 | public PaymentStatus Status { get; set; } 14 | 15 | [Required] 16 | public PaymentMethod Method { get; set; } 17 | 18 | [Required] 19 | [StringLength(100)] 20 | public string Name { get; set; } 21 | 22 | [Required] 23 | [DataType(DataType.CreditCard)] 24 | [StringLength(20)] 25 | public string Number { get; set; } 26 | 27 | [Required] 28 | [DataType(DataType.Date)] 29 | public DateTime ExpDate { get; set; } 30 | 31 | [Required] 32 | [Range(1, 999)] 33 | public int CVV { get; set; } 34 | 35 | /// 36 | /// Basic override for logging purposes 37 | /// 38 | /// 39 | public override string ToString() => 40 | $"Name: {Name}, Type: {Method}, Number: {Number.MaskCC()}, Exp: {ExpDate.ToExpDate()}"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/OrderSvc/Models/PaymentMethod.cs: -------------------------------------------------------------------------------- 1 | namespace OrderSvc.Models 2 | { 3 | public enum PaymentMethod 4 | { 5 | MasterCard, 6 | Visa, 7 | Amex 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/OrderSvc/Models/PaymentStatus.cs: -------------------------------------------------------------------------------- 1 | namespace OrderSvc.Models 2 | { 3 | public enum PaymentStatus 4 | { 5 | Pending, 6 | Authorized, 7 | Declined, 8 | Cancelled, 9 | Refunded 10 | } 11 | } -------------------------------------------------------------------------------- /src/OrderSvc/Models/ShippingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace OrderSvc.Models 2 | { 3 | public enum ShippingStatus 4 | { 5 | Pending, 6 | Delivered, 7 | Cancelled 8 | } 9 | } -------------------------------------------------------------------------------- /src/OrderSvc/OrderSvc.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/OrderSvc/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace OrderSvc 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/OrderSvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8003", 7 | "sslPort": 41403 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "OrderSvc": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/OrderSvc/Repositories/EventType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace OrderSvc.Repositories 7 | { 8 | /// 9 | /// Defines the events that can be registered on the event_type table 10 | /// 11 | public enum EventType 12 | { 13 | OrderCreated = 1, 14 | OrderUpdated, 15 | OrderCancelled, 16 | OrderApproved, 17 | OrderShipped, 18 | OrderClosed, 19 | PaymentSubmitted = 10, 20 | PaymentRequested, 21 | PaymentUpdated, 22 | PaymentAuthorized, 23 | PaymentDeclined, 24 | ShippingInfoSubmitted = 20, 25 | ShippingInfoUpdated, 26 | ShippingInfoRemoved 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/OrderSvc/Repositories/IOrderRepository.cs: -------------------------------------------------------------------------------- 1 | using OrderSvc.Models; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace OrderSvc.Repositories 6 | { 7 | public interface IOrderRepository 8 | { 9 | Task> GetOrdersByAccountId(int accountId); 10 | Task Insert(Order order); 11 | Task GetById(int id, bool fullGraph = false); 12 | Task GetByNumber(string number, bool fullGraph = false); 13 | Task Update(int id, OrderStatus orderStatus, PaymentStatus paymentStatus, ShippingStatus shippingStatus); 14 | } 15 | } -------------------------------------------------------------------------------- /src/OrderSvc/Repositories/RefType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace OrderSvc.Repositories 7 | { 8 | public enum RefType 9 | { 10 | Address, 11 | PaymentInfo, 12 | ShippingInfo 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OrderSvc/Services/IOrderSvc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Microservices.Core.Contracts.Payment; 4 | using Microservices.Core.Contracts.Shipping; 5 | using OrderSvc.Models; 6 | 7 | namespace OrderSvc.Services 8 | { 9 | public interface IOrderSvc 10 | { 11 | Task SubmitOrder(Order order); 12 | Task GetOrderByNumber(string number); 13 | Task GetOrderById(int id); 14 | Task> GetOrdersByAccountId(int accountId); 15 | Task OnPaymentProcessed(PaymentResponse message); 16 | Task OnShippingProcessed(ShippingResponse message); 17 | } 18 | } -------------------------------------------------------------------------------- /src/OrderSvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "MassTransit": { 10 | "Host": "rabbitmq://localhost", 11 | "Queue": "order" 12 | }, 13 | "ConnectionString": "Server=localhost;Port=3303;Database=orderdb;Uid=root;Pwd=todo" 14 | } 15 | -------------------------------------------------------------------------------- /src/PaymentSvc/Consumers/PaymentRequestConsumer.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Payment; 2 | using MassTransit; 3 | using PaymentSvc.Services; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace PaymentSvc.Consumers 10 | { 11 | /// 12 | /// Requests a new payment from the Payment service 13 | /// 14 | public class PaymentRequestConsumer 15 | : IConsumer 16 | { 17 | 18 | readonly IPaymentSvc _svc; 19 | 20 | public PaymentRequestConsumer(IPaymentSvc svc) 21 | { 22 | _svc = svc; 23 | } 24 | 25 | public async Task Consume(ConsumeContext context) 26 | { 27 | await _svc.RequestPayment(context.Message); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/PaymentSvc/Controllers/PaymentController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using PaymentSvc.Models; 6 | using PaymentSvc.Services; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Configuration; 9 | 10 | namespace PaymentSvc.Controllers 11 | { 12 | [Route("[controller]/[action]")] 13 | [ApiController] 14 | public class PaymentController : ControllerBase 15 | { 16 | readonly IPaymentSvc _svc; 17 | const string help = @"The Payment service is alive! Try GET /api/v1/payments/{id}."; 18 | 19 | public PaymentController(IPaymentSvc svc) 20 | { 21 | _svc = svc; 22 | } 23 | 24 | [Route("/ping")] 25 | public IActionResult Ping() 26 | { 27 | return Ok(); 28 | } 29 | 30 | [Route("/help")] 31 | public IActionResult Help() 32 | { 33 | return Ok(help); 34 | } 35 | 36 | [Route("/api/v1/payments/{id}")] 37 | public async Task GetPaymentById(string id) 38 | { 39 | return await _svc.GetById(id); 40 | } 41 | 42 | [Route("/api/v1/payments/search")] 43 | public async Task GetPaymentByAccountId(string accountId) 44 | { 45 | return await _svc.GetByAccountId(accountId); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/PaymentSvc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "PaymentSvc.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/PaymentSvc/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | 3 | namespace PaymentSvc.Infrastructure.Options 4 | { 5 | public class AppConfig 6 | { 7 | public MassTransitOptions MassTransit { get; set; } 8 | public string ConnectionString { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/PaymentSvc/Models/Payment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microservices.Core.Contracts.Payment; 3 | 4 | namespace PaymentSvc.Models 5 | { 6 | public class Payment 7 | { 8 | public int Id { get; set; } 9 | 10 | public int AccountId { get; set; } 11 | 12 | public int OrderId { get; set; } 13 | 14 | public decimal Amount { get; set; } 15 | 16 | public string Currency { get; set; } 17 | 18 | public PaymentStatus Status { get; set; } 19 | 20 | public PaymentMethod Method { get; set; } 21 | 22 | public string AuthCode { get; set; } 23 | 24 | public static Payment Parse(PaymentRequest pr) 25 | { 26 | if (pr == null) 27 | return null; 28 | 29 | // todo :: automapper 30 | return new Payment 31 | { 32 | AccountId = pr.AccountId, 33 | Currency = pr.Currency, 34 | OrderId = pr.OrderId, 35 | Amount = pr.Amount, 36 | Method = Enum.Parse(pr.Method, true) 37 | }; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/PaymentSvc/Models/PaymentGatewayRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace PaymentSvc.Models 7 | { 8 | public class PaymentGatewayRequest 9 | { 10 | public int Id { get; set; } 11 | 12 | public int PaymentId { get; set; } 13 | 14 | /// 15 | /// A random value to emulate a payment gateway Id 16 | /// 17 | public string PaymentGatewayId { get; set; } = Guid.NewGuid().ToString(); 18 | 19 | public string Currency { get; set; } 20 | 21 | public decimal Amount { get; set; } 22 | 23 | public PaymentMethod Method { get; set; } 24 | 25 | public string Name { get; set; } 26 | 27 | public string Number { get; set; } 28 | 29 | public DateTime ExpDate { get; set; } 30 | 31 | public int CVV { get; set; } 32 | 33 | public string AuthCode { get; set; } 34 | 35 | public PaymentGatewayResponseStatus Status { get; set; } 36 | 37 | public PaymentStatus FakeStatus { get; set; } 38 | 39 | public int FakeDelay { get; set; } = 5000; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/PaymentSvc/Models/PaymentGatewayResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace PaymentSvc.Models 7 | { 8 | public class PaymentGatewayResponse 9 | { 10 | public string PaymentGatewayId { get; set; } 11 | 12 | public string Currency { get; set; } 13 | 14 | public decimal Amount { get; set; } 15 | 16 | public string AuthCode { get; set; } 17 | 18 | public PaymentGatewayResponseStatus Status { get; set; } 19 | 20 | public DateTime RequestedOn { get; set; } = DateTime.UtcNow; 21 | 22 | /// 23 | /// Just another fake val in the future 24 | /// 25 | public DateTime ProcessedOn { get; set; } = DateTime.UtcNow.AddSeconds(13); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/PaymentSvc/Models/PaymentGatewayResponseStatus.cs: -------------------------------------------------------------------------------- 1 | namespace PaymentSvc.Models 2 | { 3 | public enum PaymentGatewayResponseStatus 4 | { 5 | Pending, 6 | Authorized, 7 | Declined, 8 | Cancelled, 9 | Refunded, 10 | Invalid, 11 | Error 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/PaymentSvc/Models/PaymentMethod.cs: -------------------------------------------------------------------------------- 1 | namespace PaymentSvc.Models 2 | { 3 | public enum PaymentMethod 4 | { 5 | MasterCard, 6 | Visa, 7 | Amex 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/PaymentSvc/Models/PaymentStatus.cs: -------------------------------------------------------------------------------- 1 | namespace PaymentSvc.Models 2 | { 3 | public enum PaymentStatus 4 | { 5 | Pending, 6 | Authorized, 7 | Declined, 8 | Cancelled, 9 | Refunded, 10 | Invalid, 11 | Error 12 | } 13 | } -------------------------------------------------------------------------------- /src/PaymentSvc/PaymentSvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/PaymentSvc/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace PaymentSvc 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/PaymentSvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8007", 7 | "sslPort": 41407 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "PaymentSvc": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/PaymentSvc/Repositories/IPaymentRepository.cs: -------------------------------------------------------------------------------- 1 | using PaymentSvc.Models; 2 | using PaymentSvc.Services; 3 | using System.Threading.Tasks; 4 | 5 | namespace PaymentSvc.Repositories 6 | { 7 | public interface IPaymentRepository 8 | { 9 | Task InsertPayment(Payment pmt); 10 | Task UpdatePayment(Payment pmt); 11 | Task InsertPaymentRequest(PaymentGatewayRequest pgr); 12 | Task GetById(string id); 13 | Task GetByAccountId(string email); 14 | } 15 | } -------------------------------------------------------------------------------- /src/PaymentSvc/Services/IPaymentGateway.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Payment; 2 | using PaymentSvc.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace PaymentSvc.Services 9 | { 10 | public interface IPaymentGateway 11 | { 12 | Task Process(PaymentGatewayRequest pgr); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/PaymentSvc/Services/IPaymentSvc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Microservices.Core.Contracts.Payment; 4 | using PaymentSvc.Models; 5 | 6 | namespace PaymentSvc.Services 7 | { 8 | public interface IPaymentSvc 9 | { 10 | Task RequestPayment(PaymentRequest paymentRequestccount); 11 | Task GetById(string id); 12 | Task GetByAccountId(string accountId); 13 | } 14 | } -------------------------------------------------------------------------------- /src/PaymentSvc/Services/PaymentGateway.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Payment; 2 | using Microservices.Core.Infrastructure.Extensions; 3 | using PaymentSvc.Models; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace PaymentSvc.Services 10 | { 11 | public class PaymentGateway : IPaymentGateway 12 | { 13 | public async Task Process(PaymentGatewayRequest pgr) 14 | { 15 | // fake pmt delay 16 | await Task.Delay(pgr.FakeDelay); 17 | 18 | return BuildFakeResponse(pgr); 19 | } 20 | 21 | private PaymentGatewayResponse BuildFakeResponse(PaymentGatewayRequest pgr) 22 | { 23 | var approved = pgr.FakeStatus == Models.PaymentStatus.Authorized; 24 | return new PaymentGatewayResponse 25 | { 26 | PaymentGatewayId = pgr.PaymentGatewayId, 27 | Currency = pgr.Currency, 28 | Amount = pgr.Amount, 29 | Status = pgr.FakeStatus.Parse(), 30 | AuthCode = approved ? 31 | Guid.NewGuid().ToString().ToUpper().Substring(0, 8) : 32 | null, 33 | }; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/PaymentSvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "MassTransit": { 10 | "Host": "rabbitmq://localhost", 11 | "Queue": "payment" 12 | }, 13 | "ConnectionString": "Server=localhost;Port=3307;Database=paymentdb;Uid=root;Pwd=todo" 14 | } 15 | -------------------------------------------------------------------------------- /src/PaymentSvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "MassTransit": { 11 | "Host": "rabbitmq://rabbitmq", 12 | "Queue": "payment" 13 | }, 14 | "ConnectionString": "Server=payment-db;Port=3306;Database=paymentdb;Uid=root;Pwd=todo" 15 | } 16 | -------------------------------------------------------------------------------- /src/PaymentSvc/db.sql: -------------------------------------------------------------------------------- 1 | -- Payment database init script 2 | create database if not exists paymentdb; 3 | use paymentdb; 4 | 5 | create table if not exists payment ( 6 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 7 | account_id INT NOT NULL, 8 | order_id INT NOT NULL, 9 | amount DECIMAL(10,2) NOT NULL, 10 | currency VARCHAR(3) NOT NULL, 11 | method TINYINT NOT NULL, 12 | status TINYINT NOT NULL, 13 | auth_code VARCHAR(10) NULL, 14 | created_at DATETIME NOT NULL, 15 | last_modified DATETIME NOT NULL 16 | ); 17 | 18 | create table if not exists payment_request ( 19 | pmt_gateway_id VARCHAR(37) NOT NULL PRIMARY KEY, 20 | pmt_id INT NOT NULL, 21 | amount DECIMAL(10,2) NOT NULL, 22 | currency VARCHAR(3) NOT NULL, 23 | name VARCHAR(1000) NOT NULL, 24 | number VARCHAR(40) NOT NULL, 25 | cvv INT NOT NULL, 26 | exp_date DATETIME NOT NULL, 27 | method TINYINT NOT NULL, 28 | status TINYINT NOT NULL, 29 | auth_code VARCHAR(10) NULL, 30 | created_at DATETIME NOT NULL, 31 | FOREIGN KEY (pmt_id) 32 | REFERENCES payment(id) 33 | ); -------------------------------------------------------------------------------- /src/RecommendationSvc/Consumers/OrderSubmittedConsumer.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Orders; 2 | using MassTransit; 3 | using RecommendationSvc.Services; 4 | using System.Threading.Tasks; 5 | 6 | namespace RecommendationSvc.Consumers 7 | { 8 | public class OrderSubmittedConsumer : IConsumer 9 | { 10 | 11 | readonly IRecommendationSvc _svc; 12 | 13 | public OrderSubmittedConsumer(IRecommendationSvc svc) 14 | { 15 | _svc = svc; 16 | } 17 | 18 | public async Task Consume(ConsumeContext context) 19 | { 20 | await _svc.BuildRecommendation(context.Message); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/RecommendationSvc/Controllers/RecommendationController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using RecommendationSvc.Services; 3 | using System.Threading.Tasks; 4 | 5 | namespace RecommendationSvc.Controllers 6 | { 7 | [Route("[controller]/[action]")] 8 | [ApiController] 9 | public class RecommendationController : ControllerBase 10 | { 11 | readonly IRecommendationSvc _svc; 12 | const string help = @"The Recommendation service is alive! Try GET /api/v1/recommendations/{product-slug}"; 13 | 14 | public RecommendationController(IRecommendationSvc svc) 15 | { 16 | _svc = svc; 17 | } 18 | 19 | [Route("/ping")] 20 | public IActionResult Ping() 21 | { 22 | return Ok(); 23 | } 24 | 25 | [Route("/help")] 26 | public IActionResult Help() 27 | { 28 | return Ok(help); 29 | } 30 | 31 | [Route("/api/v1/recommendations/{slug}")] 32 | public async Task GetByProductSlug(string slug) 33 | { 34 | return Ok(await _svc.GetByProductSlug(slug)); 35 | } 36 | 37 | [Route("/api/v1/recommendations/account/{accountId}")] 38 | public async Task GetByAccountId(string accountId) 39 | { 40 | return Ok(await _svc.GetByAccountId(accountId)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/RecommendationSvc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "RecommendationSvc.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/RecommendationSvc/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | 3 | namespace RecommendationSvc.Infrastructure.Options 4 | { 5 | public class AppConfig 6 | { 7 | public MassTransitOptions MassTransit { get; set; } 8 | public string ConnectionString { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/RecommendationSvc/Models/Recommendation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RecommendationSvc.Models 6 | { 7 | public class Recommendation 8 | { 9 | public string Slug { get; set; } 10 | public string Name { get; set; } 11 | public string Description { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/RecommendationSvc/Models/RecommendationDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace RecommendationSvc.Models 7 | { 8 | /// 9 | /// Defines a simple recommendation dto to insert into the recommendation table 10 | /// 11 | public class RecommendationDto 12 | { 13 | public string ProductSlug { get; set; } 14 | public string RelatedSlug { get; set; } 15 | public int Hits { get; set; } 16 | public DateTime LastUpdate => DateTime.UtcNow; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/RecommendationSvc/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace RecommendationSvc 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/RecommendationSvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8005", 7 | "sslPort": 41405 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "RecommendationSvc": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/RecommendationSvc/RecommendationSvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/RecommendationSvc/Repositories/IRecommendationRepository.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Catalog; 2 | using RecommendationSvc.Models; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace RecommendationSvc.Repositories 7 | { 8 | public interface IRecommendationRepository 9 | { 10 | Task> GetByProductSlug(string slug); 11 | Task> GetByAccountId(string accountId); 12 | Task InsertProducts(List productInfos); 13 | Task InsertRecommendations(List recomms); 14 | } 15 | } -------------------------------------------------------------------------------- /src/RecommendationSvc/Services/IRecommendationSvc.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Orders; 2 | using RecommendationSvc.Models; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace RecommendationSvc.Services 7 | { 8 | public interface IRecommendationSvc 9 | { 10 | Task> GetByProductSlug(string slug); 11 | Task> GetByAccountId(string accountId); 12 | Task BuildRecommendation(OrderSubmitted message); 13 | } 14 | } -------------------------------------------------------------------------------- /src/RecommendationSvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "MassTransit": { 10 | "Host": "rabbitmq://localhost", 11 | "Queue": "recommendation" 12 | }, 13 | "ConnectionString": "Server=localhost;Port=3305;Database=recommendationdb;Uid=root;Pwd=todo" 14 | } 15 | -------------------------------------------------------------------------------- /src/RecommendationSvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "MassTransit": { 11 | "Host": "rabbitmq://rabbitmq", 12 | "Queue": "recommendation" 13 | }, 14 | "ConnectionString": "Server=recommendation-db;Port=3306;Database=recommendationdb;Uid=root;Pwd=todo" 15 | } 16 | -------------------------------------------------------------------------------- /src/ShippingSvc/Consumers/ShippingRequestConsumer.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Contracts.Shipping; 2 | using MassTransit; 3 | using ShippingSvc.Services; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace ShippingSvc.Consumers 10 | { 11 | /// 12 | /// Requests a new Shipping from the Shipping service 13 | /// 14 | public class ShippingRequestConsumer 15 | : IConsumer 16 | { 17 | 18 | readonly IShippingSvc _svc; 19 | 20 | public ShippingRequestConsumer(IShippingSvc svc) 21 | { 22 | _svc = svc; 23 | } 24 | 25 | public async Task Consume(ConsumeContext context) 26 | { 27 | await _svc.RequestShipping(context.Message); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ShippingSvc/Controllers/ShippingController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using ShippingSvc.Models; 3 | using ShippingSvc.Services; 4 | using System.Threading.Tasks; 5 | 6 | namespace ShippingSvc.Controllers 7 | { 8 | [Route("[controller]/[action]")] 9 | [ApiController] 10 | public class ShippingController : ControllerBase 11 | { 12 | readonly IShippingSvc _svc; 13 | const string help = @"The Shipping service is alive! Try GET /api/v1/shippings/{id}"; 14 | 15 | public ShippingController(IShippingSvc svc) 16 | { 17 | _svc = svc; 18 | } 19 | 20 | [Route("/ping")] 21 | public IActionResult Ping() 22 | { 23 | return Ok(); 24 | } 25 | 26 | [Route("/help")] 27 | public IActionResult Help() 28 | { 29 | return Ok(help); 30 | } 31 | 32 | [Route("/api/v1/shippings/{id}")] 33 | public async Task GetShippingById(string id) 34 | { 35 | return await _svc.GetById(id); 36 | } 37 | 38 | [Route("/api/v1/shippings/search")] 39 | public async Task GetShippingByAccountId(string accountId) 40 | { 41 | return await _svc.GetByAccountId(accountId); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ShippingSvc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "ShippingSvc.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/ShippingSvc/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | 3 | namespace ShippingSvc.Infrastructure.Options 4 | { 5 | public class AppConfig 6 | { 7 | public MassTransitOptions MassTransit { get; set; } 8 | public string ConnectionString { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ShippingSvc/Models/ShippingProvider.cs: -------------------------------------------------------------------------------- 1 | namespace ShippingSvc.Models 2 | { 3 | public enum ShippingProvider 4 | { 5 | Fedex, 6 | UPS, 7 | DHL, 8 | CanadaPost 9 | } 10 | } -------------------------------------------------------------------------------- /src/ShippingSvc/Models/ShippingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace ShippingSvc.Models 2 | { 3 | public enum ShippingStatus 4 | { 5 | Pending, 6 | Delivered, 7 | Declined, 8 | Cancelled, 9 | Invalid, 10 | Error 11 | } 12 | } -------------------------------------------------------------------------------- /src/ShippingSvc/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace ShippingSvc 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/ShippingSvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8008", 7 | "sslPort": 41408 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "ShippingSvc": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/ShippingSvc/Repositories/IShippingRepository.cs: -------------------------------------------------------------------------------- 1 | using ShippingSvc.Models; 2 | using ShippingSvc.Services; 3 | using System.Threading.Tasks; 4 | 5 | namespace ShippingSvc.Repositories 6 | { 7 | public interface IShippingRepository 8 | { 9 | Task Insert(Shipping pmt); 10 | Task GetById(string id); 11 | Task GetByAccountId(string email); 12 | } 13 | } -------------------------------------------------------------------------------- /src/ShippingSvc/Services/IShippingSvc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Microservices.Core.Contracts.Shipping; 4 | using ShippingSvc.Models; 5 | 6 | namespace ShippingSvc.Services 7 | { 8 | public interface IShippingSvc 9 | { 10 | Task RequestShipping(ShippingRequest shippingRequest); 11 | Task GetById(string id); 12 | Task GetByAccountId(string accountId); 13 | } 14 | } -------------------------------------------------------------------------------- /src/ShippingSvc/ShippingSvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/ShippingSvc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "MassTransit": { 10 | "Host": "rabbitmq://localhost", 11 | "Queue": "shipping" 12 | }, 13 | "ConnectionString": "Server=localhost;Port=3308;Database=shippingdb;Uid=root;Pwd=todo" 14 | } 15 | -------------------------------------------------------------------------------- /src/ShippingSvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "MassTransit": { 11 | "Host": "rabbitmq://rabbitmq", 12 | "Queue": "shipping" 13 | }, 14 | "ConnectionString": "Server=shipping-db;Port=3306;Database=shippingdb;Uid=root;Pwd=todo" 15 | } 16 | -------------------------------------------------------------------------------- /src/ShippingSvc/db.sql: -------------------------------------------------------------------------------- 1 | -- Shipping Database 2 | create database if not exists shippingdb; 3 | use shippingdb; 4 | 5 | CREATE TABLE if not exists shipping ( 6 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 7 | number VARCHAR(100) NOT NULL, 8 | account_id INT NOT NULL, 9 | order_id INT NOT NULL, 10 | name VARCHAR(1000) NOT NULL COMMENT 'Name of the recipient', 11 | amount DECIMAL(10,2) NOT NULL, 12 | currency VARCHAR(3) NOT NULL, 13 | street VARCHAR(1000) NOT NULL, 14 | city VARCHAR(300) NOT NULL, 15 | region VARCHAR(100) NOT NULL, 16 | postal_code VARCHAR(10) NOT NULL, 17 | country VARCHAR(100) NOT NULL, 18 | status TINYINT NOT NULL, 19 | provider TINYINT NOT NULL, 20 | created_at DATETIME NOT NULL 21 | ); 22 | -------------------------------------------------------------------------------- /src/Web/Controllers/HomeController.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.Extensions.Logging; 8 | using Web.Models; 9 | using Web.Services; 10 | 11 | namespace Web.Controllers 12 | { 13 | public class HomeController : Controller 14 | { 15 | 16 | public IActionResult Index() 17 | { 18 | return View(); 19 | } 20 | 21 | [Route("/cheatsheet")] 22 | public IActionResult Cheatsheet() 23 | { 24 | return View(); 25 | } 26 | 27 | [Route("/about")] 28 | public IActionResult About() 29 | { 30 | return View(); 31 | } 32 | 33 | [Route("/help")] 34 | public IActionResult Help() 35 | { 36 | return View(); 37 | } 38 | 39 | public IActionResult Products() 40 | { 41 | return View(); 42 | } 43 | 44 | 45 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 46 | public IActionResult Error() 47 | { 48 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Web/Controllers/NewsletterController.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.Extensions.Logging; 8 | using Web.Models; 9 | using Web.Services; 10 | 11 | namespace Web.Controllers 12 | { 13 | public class NewsletterController : Controller 14 | { 15 | readonly INewsletterProxy _svc; 16 | 17 | public NewsletterController(INewsletterProxy svc) 18 | { 19 | _svc = svc; 20 | } 21 | 22 | /// 23 | /// Receives a newsletter subscription via the Newsletter microservice on the /signup endpoint 24 | /// 25 | /// 26 | /// 27 | [HttpPost] 28 | [Route("/api/signup")] 29 | public async Task Signup([FromBody] NewsletterSignUp signup) 30 | { 31 | if (signup == null || !signup.IsValid()) 32 | return BadRequest(); 33 | 34 | await _svc.Signup(signup); 35 | 36 | return Ok(); 37 | } 38 | 39 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 40 | public IActionResult Error() 41 | { 42 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Web/Controllers/RecommendationController.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 Web.Models; 8 | using Web.Services; 9 | 10 | namespace Web.Controllers 11 | { 12 | public class RecommendationController : Controller 13 | { 14 | readonly IRecommendationProxy _svc; 15 | 16 | public RecommendationController(IRecommendationProxy svc) 17 | { 18 | _svc = svc; 19 | } 20 | 21 | 22 | /// 23 | /// Provides the list of recommendations based on product slug 24 | /// 25 | [Route("/api/recommendations/{slug}")] 26 | public async Task GetRecommendations(string slug) 27 | { 28 | return Ok(await _svc.GetByProductSlug(slug)); 29 | } 30 | 31 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 32 | public IActionResult Error() 33 | { 34 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | ARG GITHUB_API_KEY 4 | 5 | # Copy csproj and restore as distinct layers 6 | COPY *.csproj ./ 7 | COPY nuget.config ./ 8 | RUN dotnet restore 9 | 10 | # Copy everything else and build 11 | COPY . ./ 12 | RUN dotnet publish -c Release -o out 13 | 14 | # Build runtime image 15 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 16 | WORKDIR /app 17 | COPY --from=build-env /app/out . 18 | ENTRYPOINT ["dotnet", "Web.dll"] 19 | 20 | -------------------------------------------------------------------------------- /src/Web/Infrastructure/Global/Site.cs: -------------------------------------------------------------------------------- 1 | using Web.Infrastructure.Options; 2 | 3 | namespace Web.Infrastructure.Global 4 | { 5 | public static class Site 6 | { 7 | public static StoreSettings StoreSettings { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Web/Infrastructure/Options/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Options; 2 | using System.Collections.Generic; 3 | 4 | namespace Web.Infrastructure.Options 5 | { 6 | public class AppConfig 7 | { 8 | public StoreSettings StoreSettings { get; set; } 9 | public ServiceConfig Services { get; set; } 10 | public RedisOptions Redis { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Web/Infrastructure/Options/ServiceConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Web.Infrastructure.Options 4 | { 5 | public class ServiceConfig 6 | { 7 | public string Catalog { get; set; } 8 | public string Newsletter { get; set; } 9 | public string Order { get; set; } 10 | public string Account { get; set; } 11 | public string Recommendation { get; set; } 12 | public string Notification { get; set; } 13 | public string Payment { get; set; } 14 | public string Shipping { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Web/Infrastructure/Options/StoreSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Web.Infrastructure.Options 4 | { 5 | public class StoreSettings 6 | { 7 | public string Country { get; set; } 8 | public List Regions { get; set; } 9 | public string Currency { get; set; } 10 | public string CurrencyDisplay { get; set; } 11 | public string StoreName { get; set; } 12 | public string StoreUrl { get; set; } 13 | public decimal Tax { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Web/Models/Account/Account.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace Web.Models.Account 7 | { 8 | public class Account 9 | { 10 | public string Id { get; set; } 11 | 12 | [Required] 13 | [StringLength(100)] 14 | public string Name { get; set; } 15 | 16 | [Required] 17 | [EmailAddress] 18 | public string Email { get; set; } 19 | 20 | [Required] 21 | [StringLength(100)] 22 | public string Street { get; set; } 23 | 24 | [Required] 25 | [StringLength(10)] 26 | public string PostalCode { get; set; } 27 | 28 | [Required] 29 | [StringLength(50)] 30 | public string City { get; set; } 31 | 32 | 33 | [Required] 34 | [StringLength(2)] 35 | public string Region { get; set; } 36 | 37 | [Required] 38 | [StringLength(20)] 39 | public string Country { get; set; } 40 | 41 | [Required] 42 | [StringLength(50)] 43 | public string Password { get; set; } 44 | 45 | public bool SubscribedToNewsletter { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Web/Models/Account/AccountDetails.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Web.Models.Account 4 | { 5 | public class AccountDetails 6 | { 7 | public string Id { get; set; } 8 | 9 | [Required] 10 | [StringLength(100)] 11 | public string Name { get; set; } 12 | 13 | [Required] 14 | [EmailAddress] 15 | public string Email { get; set; } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Web/Models/Account/AccountHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Web.Models.Account 5 | { 6 | public class AccountHistory 7 | { 8 | public string Info { get; set; } 9 | 10 | public DateTime CreatedAt { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Web/Models/Account/Address.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microservices.Core.Infrastructure.Extensions; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Text; 6 | 7 | namespace Web.Models.Account 8 | { 9 | public class Address 10 | { 11 | public int Id { get; set; } 12 | 13 | public int AccountId { get; set; } 14 | 15 | public bool IsDefault { get; set; } 16 | 17 | public DateTime CreatedOn { get; set; } = DateTime.UtcNow; 18 | 19 | public DateTime LastUpdated { get; set; } = DateTime.UtcNow; 20 | 21 | [Required] 22 | [StringLength(100)] 23 | public string Name { get; set; } 24 | 25 | [Required] 26 | [StringLength(100)] 27 | public string Street { get; set; } 28 | 29 | [Required] 30 | [StringLength(10)] 31 | public string PostalCode { get; set; } 32 | 33 | [Required] 34 | [StringLength(50)] 35 | public string City { get; set; } 36 | 37 | [Required] 38 | [StringLength(2)] 39 | public string Region { get; set; } 40 | 41 | [Required] 42 | [StringLength(20)] 43 | public string Country { get; set; } 44 | 45 | // simple validation 46 | public bool IsValid() => 47 | Name.HasValue(3) && 48 | PostalCode.HasValue(3) && 49 | City.HasValue(3) && 50 | Region.HasValue(2) && 51 | Country.HasValue(2); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Web/Models/Account/ChangePassword.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Web.Models.Account 4 | { 5 | public class UpdatePassword 6 | { 7 | public string AccountId { get; set; } 8 | 9 | [Required] 10 | [StringLength(50)] 11 | [DataType(DataType.Password)] 12 | public string CurrentPassword { get; set; } 13 | 14 | [Required] 15 | [StringLength(50)] 16 | [DataType(DataType.Password)] 17 | public string NewPassword { get; set; } 18 | 19 | [Required] 20 | [StringLength(50)] 21 | [DataType(DataType.Password)] 22 | [Compare("NewPassword")] 23 | public string ConfirmPassword { get; set; } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Web/Models/Account/SignIn.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Extensions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Text; 6 | 7 | namespace Web.Models.Account 8 | { 9 | public class SignIn 10 | { 11 | [Required] 12 | [EmailAddress] 13 | [Display(Name = "Email Address")] 14 | public string Email { get; set; } 15 | 16 | [Required] 17 | [DataType(DataType.Password)] 18 | public string Password { get; set; } 19 | 20 | public bool IsValid() => 21 | Email.HasValue(6) && Password.HasValue(3); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Web/Models/Catalog/Category.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Web.Models.Catalog 6 | { 7 | public class Category 8 | { 9 | public string Id { get; set; } 10 | public string Name { get; set; } 11 | public string Slug { get; set; } 12 | public string Description { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Web/Models/Catalog/Product.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Web.Models.Catalog 6 | { 7 | public class Product 8 | { 9 | public string Slug { get; set; } 10 | public string Name { get; set; } 11 | public string Description { get; set; } 12 | public float Price { get; set; } 13 | public string Currency { get; set; } 14 | public float OrderTotal { get; set; } 15 | public float Taxes { get; set; } 16 | public float Total { get; set; } 17 | public string Url { get; set; } 18 | public string CategoryId { get; set; } 19 | public string CategoryName { get; set; } 20 | public int Rating { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Web/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Web.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Web/Models/Newsletter/NewsletterSignUp.cs: -------------------------------------------------------------------------------- 1 | using Microservices.Core.Infrastructure.Extensions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Web.Models 7 | { 8 | public class NewsletterSignUp 9 | { 10 | public string Name { get; set; } 11 | public string Email { get; set; } 12 | 13 | public bool IsValid() => 14 | Name.HasValue(3) && 15 | Email.HasValue(6); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Web/Models/Order/CartReview.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Web.Models.Account; 3 | using Acct = Web.Models.Account; 4 | 5 | namespace Web.Models.Order 6 | { 7 | public class CartReview 8 | { 9 | public Acct.Account Account { get; set; } 10 | public Address Address { get; set; } 11 | public PaymentInfo PaymentInfo { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Web/Models/Order/LineItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text; 5 | 6 | namespace Web.Models.Order 7 | { 8 | public class LineItem 9 | { 10 | [Required] 11 | public string Name { get; set; } 12 | 13 | [Required] 14 | public string Slug { get; set; } 15 | 16 | [Range(0,100000)] 17 | [Required] 18 | public decimal Price { get; set; } 19 | 20 | [Range(1, 999)] 21 | [Required] 22 | public int Qty { get; set; } = 1; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Web/Models/Order/Order.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microservices.Core.Infrastructure.Extensions; 6 | using Web.Models.Account; 7 | 8 | namespace Web.Models.Order 9 | { 10 | public class Order 11 | { 12 | public string Number { get; set; } 13 | public int AccountId { get; set; } 14 | public OrderStatus Status { get; set; } 15 | public PaymentStatus PaymentStatus { get; set; } 16 | public ShippingStatus ShippingStatus { get; set; } 17 | public DateTime CreatedOn { get; set; } = DateTime.UtcNow; 18 | public DateTime LastUpdated { get; set; } = DateTime.UtcNow; 19 | public string Currency { get; set; } 20 | public decimal Price => (LineItems.HasAny() ? LineItems.Sum(li => li.Qty * li.Price) : 0); 21 | public decimal Tax { get; set; } 22 | public decimal Discount { get; set; } 23 | public decimal Shipping { get; set; } 24 | public decimal TotalPrice => Math.Round(Price * (1 + Tax) + Shipping - Discount, 2); 25 | public List LineItems { get; set; } 26 | public PaymentInfo PaymentInfo { get; set; } 27 | public Address ShippingInfo { get; set; } 28 | 29 | public bool IsValidForSubmit() => 30 | AccountId > 0 && 31 | Currency.HasValue(3) && 32 | Tax > 0 && Tax < 1 && 33 | LineItems.HasAny() && 34 | Price > 0 && 35 | ShippingInfo.IsValid() && 36 | PaymentInfo.IsValid() && 37 | LineItems.Count > 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Web/Models/Order/OrderNumberResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Web.Models.Order 7 | { 8 | public class OrderNumberResponse 9 | { 10 | public string Number { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Web/Models/Order/OrderStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Models.Order 2 | { 3 | public enum OrderStatus 4 | { 5 | New, 6 | Submitted, 7 | PaymentApproved, 8 | PaymentDeclined, 9 | WaitingShipping, 10 | Shipped, 11 | Delivered, 12 | Complete, 13 | Cancelled 14 | } 15 | } -------------------------------------------------------------------------------- /src/Web/Models/Order/PaymentInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using Microservices.Core.Infrastructure.Extensions; 4 | 5 | namespace Web.Models.Order 6 | { 7 | public class PaymentInfo 8 | { 9 | public int Id { get; set; } 10 | 11 | public int AccountId { get; set; } 12 | 13 | public bool IsDefault { get; set; } 14 | 15 | [Required] 16 | public PaymentMethod Method { get; set; } 17 | 18 | [Required] 19 | [StringLength(100)] 20 | public string Name { get; set; } 21 | 22 | [Required] 23 | [DataType(DataType.CreditCard)] 24 | [StringLength(20)] 25 | public string Number { get; set; } 26 | 27 | [Required] 28 | [DataType(DataType.Date)] 29 | public DateTime ExpDate { get; set; } 30 | 31 | [Required] 32 | [Range(1, 999)] 33 | public int CVV { get; set; } 34 | 35 | // simple validation 36 | public bool IsValid() => 37 | Id > 0 && 38 | AccountId > 0 && 39 | Name.HasValue(3) && 40 | Number.HasValue(10) && 41 | (CVV > 0 && CVV < 999) && 42 | ExpDate > DateTime.UtcNow.AddDays(1); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Web/Models/Order/PaymentMethod.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Models.Order 2 | { 3 | public enum PaymentMethod 4 | { 5 | MasterCard, 6 | Visa, 7 | Amex 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Web/Models/Order/PaymentStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Models.Order 2 | { 3 | public enum PaymentStatus 4 | { 5 | Pending, 6 | Approved, 7 | Declined, 8 | Cancelled, 9 | Refunded 10 | } 11 | } -------------------------------------------------------------------------------- /src/Web/Models/Order/ShippingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Models.Order 2 | { 3 | public enum ShippingStatus 4 | { 5 | Pending, 6 | Delivered, 7 | Cancelled 8 | } 9 | } -------------------------------------------------------------------------------- /src/Web/Models/Order/SubmitOrder.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Web.Models.Order 4 | { 5 | public class SubmitOrder : Order 6 | { 7 | public string AddressId { get; set; } 8 | public string PaymentId { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Web/Models/Recommendation/Recommendation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Web.Models.Recommendation 6 | { 7 | public class Recommendation 8 | { 9 | public string Slug { get; set; } 10 | public string Name { get; set; } 11 | public string Description { get; set; } 12 | public float Price { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Web/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Web 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/Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8000", 7 | "sslPort": 41400 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "environmentVariables": { 14 | "ASPNETCORE_ENVIRONMENT": "Development" 15 | } 16 | }, 17 | "Web": { 18 | "commandName": "Project", 19 | "launchBrowser": true, 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Web/Services/IAccountProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using Web.Models.Account; 5 | using Web.Models.Order; 6 | 7 | namespace Web.Services 8 | { 9 | public interface IAccountProxy 10 | { 11 | Task TrySignIn(SignIn request); 12 | Task CreateAccount(Account acct); 13 | Task UpdateAccount(AccountDetails acct); 14 | Task UpdatePassword(UpdatePassword changePassword); 15 | Task GetAccountById(string acctId); 16 | Task
GetAddressById(string addrId); 17 | Task AddAddress(Address addr); 18 | Task UpdateAddress(Address addr); 19 | Task RemoveAddress(string addressId); 20 | Task> GetAddressesByAccountId(string acctId); 21 | Task SetDefaultAddress(string acctId, int addressId); 22 | Task GetPaymentInfoById(string pmtId); 23 | Task> GetPaymentInfos(string acctId); 24 | Task RemovePayment(string pmtId); 25 | Task SetDefaultPayment(string acctId, int pmtId); 26 | Task UpdatePayment(PaymentInfo pmtInfo); 27 | Task AddPayment(PaymentInfo pmtInfo); 28 | Task> GetAccountHistory(string acctId); 29 | } 30 | } -------------------------------------------------------------------------------- /src/Web/Services/ICatalogProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Web.Models; 4 | using Web.Models.Catalog; 5 | 6 | namespace Web.Services 7 | { 8 | public interface ICatalogProxy 9 | { 10 | Task> GetCategories(); 11 | Task GetCategory(string slug); 12 | Task> GetProductsByCategory(string slug); 13 | Task GetProductBySlug(string slug); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Web/Services/INewsletterProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Web.Models; 3 | 4 | namespace Web.Services 5 | { 6 | public interface INewsletterProxy 7 | { 8 | Task Signup(NewsletterSignUp signup); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Web/Services/IOrderProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Web.Models.Order; 4 | 5 | namespace Web.Services 6 | { 7 | public interface IOrderProxy 8 | { 9 | Task Submit(Order order); 10 | Task > GetOrdersByAccountId(string accountId); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Web/Services/IRecommendationProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Web.Models; 4 | using Web.Models.Recommendation; 5 | 6 | namespace Web.Services 7 | { 8 | public interface IRecommendationProxy 9 | { 10 | Task> GetByProductSlug(string slug); 11 | Task> GetByAccountId(string accountId); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Web/Services/NewsletterProxy.cs: -------------------------------------------------------------------------------- 1 | using Web.Models; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net.Http; 9 | using System.Threading.Tasks; 10 | using System.Text; 11 | using Web.Infrastructure.Base; 12 | using Microsoft.Extensions.Caching.Distributed; 13 | 14 | namespace Web.Services 15 | { 16 | public class NewsletterProxy : 17 | ProxyBase, 18 | INewsletterProxy 19 | { 20 | public NewsletterProxy( 21 | HttpClient httpClient, 22 | IConfiguration cfg, 23 | ILogger logger, 24 | IDistributedCache cache) : 25 | base(httpClient, cfg, logger, cache) 26 | { 27 | 28 | } 29 | 30 | public async Task Signup(NewsletterSignUp signup) 31 | { 32 | await PostAsync("signup", signup, "/api/v1/signup"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Web/Services/OrderProxy.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Caching.Distributed; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Net.Http; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Web.Infrastructure.Base; 11 | using Web.Models.Account; 12 | using Web.Models.Order; 13 | 14 | namespace Web.Services 15 | { 16 | 17 | public class OrderProxy : 18 | ProxyBase, 19 | IOrderProxy 20 | { 21 | public OrderProxy( 22 | HttpClient httpClient, 23 | IConfiguration cfg, 24 | ILogger logger, 25 | IDistributedCache cache) : 26 | base(httpClient, cfg, logger, cache) 27 | { 28 | // because order info is more volatile than catalog, etc 29 | // we want its cache to be refreshed more frequently 30 | cacheOptions = new DistributedCacheEntryOptions 31 | { 32 | SlidingExpiration = TimeSpan.FromSeconds(10) 33 | }; 34 | } 35 | 36 | public async Task Submit(Order order) 37 | { 38 | var on = await PostAsync( 39 | "order", order, "/api/v1/orders/submit"); 40 | 41 | order.Number = on.Number; 42 | } 43 | 44 | public async Task> GetOrdersByAccountId(string accountId) 45 | { 46 | var endpoint = $"/api/v1/orders/{accountId}"; 47 | return await GetAsync>( 48 | "accountid", accountId, endpoint); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Web/Services/RecommendationProxy.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Caching.Distributed; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | using System.Collections.Generic; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | using Web.Infrastructure.Base; 8 | using Web.Models.Recommendation; 9 | 10 | namespace Web.Services 11 | { 12 | public class RecommendationProxy : 13 | ProxyBase, 14 | IRecommendationProxy 15 | { 16 | 17 | public RecommendationProxy( 18 | HttpClient httpClient, 19 | IConfiguration cfg, 20 | ILogger logger, 21 | IDistributedCache cache) : 22 | base(httpClient, cfg, logger, cache) 23 | { 24 | 25 | } 26 | 27 | public async Task> GetByProductSlug(string slug) 28 | { 29 | var url = $"/api/v1/recommendations/{slug}"; 30 | 31 | return await GetAsync>( 32 | "product", slug, url); 33 | } 34 | 35 | public async Task> GetByAccountId(string accountId) 36 | { 37 | var url = $"/api/v1/recommendations/account/{accountId}"; 38 | 39 | return await GetAsync>( 40 | "account", accountId, url); 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Web/Views/Account/AddAddress.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Add Address"; 3 | ViewData["EditMode"] = true; 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 | @if (TempData["ErrorMsg"] != null) 9 | { 10 |
@TempData["ErrorMsg"]
11 | } 12 | 13 |
14 |
15 |
16 | 17 |
18 | Cancel 19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Web/Views/Account/AddPayment.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Add Payment"; 3 | ViewData["EditMode"] = true; 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 | @if (TempData["ErrorMsg"] != null) 9 | { 10 |
@TempData["ErrorMsg"]
11 | } 12 | 13 |
14 |
15 |
16 | 17 |
18 | Cancel 19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Web/Views/Account/Address.cshtml: -------------------------------------------------------------------------------- 1 | @using Web.Models.Account; 2 | @model List
3 | @{ 4 | ViewData["Title"] = "My Addresses"; 5 | ViewData["ShowOptions"] = "True"; 6 | } 7 | 8 |

@ViewData["Title"]

9 |

Here's your list of addresses.

10 | 11 | 12 | 13 |
14 | Back to account 15 | Add Address 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Web/Views/Account/Create.cshtml: -------------------------------------------------------------------------------- 1 | @model Web.Models.Account.Account 2 | @{ 3 | ViewData["Title"] = "Create Account"; 4 | ViewData["EditMode"] = true; 5 | ViewData["ShowPassword"] = true; 6 | } 7 | 8 | @if (TempData["ErrorMsg"] != null) 9 | { 10 |
@TempData["ErrorMsg"]
11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Web/Views/Account/Details.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Account Details"; 3 | ViewData["BtnTxt"] = "Update Account"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |

Here's your account info.

8 | 9 |
10 | 11 |
12 |
13 | Back to account 14 | Edit 15 |
16 | 17 | -------------------------------------------------------------------------------- /src/Web/Views/Account/EditAddress.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Edit Address"; 3 | ViewData["EditMode"] = true; 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 | @if (TempData["ErrorMsg"] != null) 9 | { 10 |
@TempData["ErrorMsg"]
11 | } 12 | 13 |
14 |
15 |
16 | 17 |
18 | Cancel 19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Web/Views/Account/EditPayment.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Edit Payment"; 3 | ViewData["EditMode"] = true; 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 | @if (TempData["ErrorMsg"] != null) 9 | { 10 |
@TempData["ErrorMsg"]
11 | } 12 | 13 |
14 |
15 |
16 | 17 |
18 | Cancel 19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Web/Views/Account/History.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Account History"; 3 | } 4 | 5 |
6 |

@ViewData["Title"]

7 |

These are the last events for your account.

8 | 9 |
10 | Back to Account 11 |
12 |
13 | 14 | -------------------------------------------------------------------------------- /src/Web/Views/Account/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Claims; 2 | @{ 3 | ViewData["Title"] = "My Account"; 4 | var name = User.FindFirstValue("Name"); 5 | } 6 | 7 |
8 |

@ViewData["Title"]

9 |

Welcome back, @name!

10 | 11 |
12 | Keep shopping 13 | Sign out 14 | 15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /src/Web/Views/Account/Orders.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "My orders"; 3 | } 4 | 5 |
6 |

@ViewData["Title"]

7 |

These are the existing orders for your account.

8 | 9 |
10 | Back to Account 11 |
12 |
13 | 14 | -------------------------------------------------------------------------------- /src/Web/Views/Account/Payments.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "My Payments"; 3 | ViewData["ShowOptions"] = "True"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |

Here's your list of payment types.

8 | 9 | 10 | 11 |
12 | Back to account 13 | Add Payment 14 |
15 | 16 | -------------------------------------------------------------------------------- /src/Web/Views/Account/SignIn.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Account SignIn"; 3 | } 4 | 5 | @if (TempData["Msg"] != null) 6 | { 7 | 10 | } 11 | 12 |
13 |
14 | 15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /src/Web/Views/Account/Update.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Update Account"; 3 | ViewData["EditMode"] = "True"; 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 |
9 | 10 | 11 |
12 | Cancel 13 | 14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Web/Views/Account/UpdatePassword.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Update Password"; 3 | } 4 | 5 |

@ViewData["Title"]

6 | 7 | @if (TempData["ErrorMsg"] != null) 8 | { 9 |
@TempData["ErrorMsg"]
10 | } 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Web/Views/Catalog/Product.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Product"; 3 | } 4 | 5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Web/Views/Catalog/Products.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | ViewData["Category"] = Model.Name; 4 | ViewData["CategoryId"] = Model.Id; 5 | } 6 | 7 |
8 |

@Model.Name

9 |

@(Model.Description ?? $"Here's our list of products under {Model.Name}")

10 | 11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 |
21 | 22 | -------------------------------------------------------------------------------- /src/Web/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "About ASP.NET Microservices"; 3 | } 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Web/Views/Home/Cheatsheet.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "About ASP.NET Microservices"; 3 | } 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Web/Views/Home/Help.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "About ASP.NET Microservices"; 3 | } 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Web/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Web/Views/Order/Checkout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Checkout"; 3 | ViewData["SmWidth"] = "12"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |

Please review the information below. You'll have a chance to review your cart on the next step.

8 |
9 | 10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | Continue Shopping 20 | Change Cart 21 | 22 |
23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Web/Views/Order/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Your Cart"; 3 | } 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Web/Views/Order/Submitted.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Order Submitted"; 3 | ViewData["Review"] = true; 4 | } 5 | 6 |
7 |
8 |
9 |

@ViewData["Title"]

10 |

Congratulations, your order was submitted. Here's the summary of your order:

11 |
Order Number: {{ orderNumber }}
12 |
13 |
14 |

Order not found

15 |

Sorry but the order you requested could not be found.

16 |
17 |
18 | 19 |
20 | Keep Shopping 21 | View my orders 22 |
23 |
24 | 25 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Account/_Create.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 |
4 |
5 |

Create Account

6 | 7 |
8 | 9 | 10 | 11 | 12 |

13 | Already have an account? Sign In 14 |

15 | 16 |
17 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Account/_Details.cshtml: -------------------------------------------------------------------------------- 1 | @using Web.Models.Account; 2 | @model AccountDetails 3 | 4 | @{ 5 | var editMode = (ViewData["EditMode"] ?? "").ToString() == "True"; 6 | } 7 | 8 |
9 |
10 | 11 | @if (editMode) 12 | { 13 | 14 | } 15 | else 16 | { 17 |
@Model.Name
18 | } 19 |
20 |
21 |
22 |
23 | 24 | @if (editMode) 25 | { 26 | 27 | } 28 | else 29 | { 30 |
@Model.Email
31 | } 32 |
33 |
34 | 35 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Account/_History.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
EventDate
{{ h.info }}{{ new Date(h.createdAt).toDateString() }}
17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Account/_MyAccount.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |
3 |
4 |
5 |
My Account
6 |

7 |

14 |

15 |
16 |
17 |
18 |
19 |
20 |
21 |
My Orders
22 |

23 |

26 |

27 |
28 |
29 |
30 |
31 |
32 |
33 |
Other
34 |

35 |

    36 |
  • Product Recommendations
  • 37 |
  • Newsletters
  • 38 |
  • etc..
  • 39 |
40 |

41 |
42 |
43 |
44 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Account/_SignIn.cshtml: -------------------------------------------------------------------------------- 1 | @using Web.Models.Account; 2 | @model SignIn 3 | 4 |
5 |

Sign In

6 | 7 |
@ViewData["Error"]
8 | 9 |
10 |
11 | 12 | 13 | We'll never share your email with anyone 😉. 14 |
15 |
16 | 17 | 18 |
19 | 20 |
21 |

22 | Don't have an account? Create Account. 23 |

24 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Account/_UpdatePassword.cshtml: -------------------------------------------------------------------------------- 1 | @using Web.Models.Account; 2 | @model UpdatePassword 3 | 4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | Cancel 19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Catalog/_Categories.cshtml: -------------------------------------------------------------------------------- 1 | 
2 | 3 |
4 | Error loading the catalog. Check if you can connect to the 5 | catalog service. 6 |
7 |
8 |
9 |
10 | 11 |
12 |
{{ c.name }}
13 |

Check our selection of {{ c.name }}.

14 | View all products in {{ c.name }} 15 |
16 |
17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Catalog/_FakeImg.cshtml: -------------------------------------------------------------------------------- 1 | Placeholder 2 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Catalog/_FakeThumbnails.cshtml: -------------------------------------------------------------------------------- 1 |  2 |
3 |
4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 |
12 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Catalog/_Products.cshtml: -------------------------------------------------------------------------------- 1 | @using Web.Infrastructure.Global; 2 | @{ 3 | var cur = Site.StoreSettings.CurrencyDisplay; 4 | } 5 | 6 |
7 | 8 |
9 |
10 |
11 |
12 |
{{ p.name }}
13 | 14 |

{{ shortDesc(p.description) }}

15 |

@cur {{ parseFloat(p.price).toFixed(2) }}

16 |

View

17 |
18 |
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_About.cshtml: -------------------------------------------------------------------------------- 1 | @using Web.Infrastructure.Global; 2 | 3 |

About

4 |

Help and information about the project.

5 | 6 |
7 |

Microservices included in the @Site.StoreSettings.StoreName

8 |

This project is composed of the following microservices:

9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Account.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Account Service

3 |

4 | The Account microservice 5 | manages account information. It persists its data primarily on 6 | a MySQL container and process operations such as creating accounts, 7 | changing password, granting access to the system (no JWT so far), etc. 8 |

9 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Catalog.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Catalog Service

3 |

4 | The catalog is provided by the 5 | Catalog microservice 6 | which consists of an ASP.NET Core app pulling data from a 7 | MongoDB container. The catalog is initialized with the 8 | docker-compose script. To change the catalog 9 | information, check th db.js file on the 10 | CatalogSvc project. 11 | The catalog information is cached locally 12 | from the Web microservice on a Redis container. 13 |

14 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Newsletter.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Newsletter Service

3 |

4 | The Newsletter microservice 5 | manages newsletter information. It persists email subscriptions primarily on 6 | a MySQL container. There's no default implementation for newsletter 7 | oprations such as a scheduled job, etc. Feel free to implement yours. 8 |

9 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Notification.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Email Notifications

3 |

4 | Notifications are provided via asynchronous messages 5 | exchanged via a RabbitMQ Docker container and processed by a .NET Core backend. 6 | Different events (such as newsletter signups, account registrations, etc) 7 | send an async message to the 8 | Notification microservice 9 | that's sends and email to the user using Gmail by default. 10 | The notification service also logs the history of sent messages on a 11 | dedicated MySQL database. 12 |

13 |
14 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Order.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Order Service

3 |

4 | The Order microservice 5 | manages order information and workflow. It persists its data primarily on 6 | a MySQL container and communicates to the other microservices primarily 7 | via asynchronous messages exchanged via a RabbitMQ container. 8 |

9 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Payment.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Payment Service

3 |

4 | The Payment microservice 5 | simulates a payment gateway and manages the payment workflow. 6 | It persists its data primarily on a MySQL container and communicates 7 | to the other microservices primarily via asynchronous messages exchanges 8 | via a RabbitMQ container. 9 |

10 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Recommendation.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Recommendation Service

3 |

4 | The Recommendation microservice 5 | simulates a recommendation service by implementing naive recommendations 6 | based on previously completed shopping carts. 7 | It persists its data primarily on a MySQL container and communicates 8 | to the other microservices primarily via asynchronous messages exchanges 9 | via a RabbitMQ container. 10 |

11 |
12 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Shipping.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Shipping Service

3 |

4 | The Shipping microservice 5 | simulates a shipping service and is reached via a RabbitMQ queue 6 | and MassTransit. The service is triggered by the order workflow 7 | whenever shopping carts are approved from the fake 8 | Payment microservice. 9 | The service persists its data primarily on a MySQL container and communicates 10 | to the other microservices primarily via asynchronous messages exchanges 11 | via a RabbitMQ container. 12 |

13 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Help/_Web.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Web

3 |

4 | The Web microservice 5 | is this ASP.NET app that you're accessing at the moment. 6 | It's essentially a BFF (backend for frontend) / Api Gateway 7 | and is the public interface between the user and the other 8 | microservices. Web caches catalog, recommendation and data 9 | from other services on a Redis container. 10 |

11 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Home/_Header.cshtml: -------------------------------------------------------------------------------- 1 |  2 |
3 |

ASP.NET Microservices

4 |

5 | This is a sample application to demo Microservices in .NET 6 | using ASP.NET Core, Docker, Redis, MongoDB, MySQL, Vue.js, MassTransit, 7 | Azure and Kubernetes.
8 | The source code is 9 | MIT licensed 10 | and is available at 11 | github.com/hd9/aspnet-microservices. 12 |

13 | 14 | To learn more about this app, microservices, Docker, Azure and Kubernetes, 15 | check: blog.hildenco.com 16 | 17 |
18 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Home/_MainContent.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Claims; 2 | @{ 3 | var nlSubscribed = User.FindFirstValue("NewsletterSubscribed") == "True"; 4 | } 5 | 6 |
7 |
8 | 9 |
10 |
11 | @if (!User.Identity.IsAuthenticated) 12 | { 13 | 14 | } 15 | else 16 | { 17 | 18 | } 19 | 20 | 21 | 22 | @if (!nlSubscribed) 23 | { 24 | 25 | } 26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Home/_SignIn.cshtml: -------------------------------------------------------------------------------- 1 |  2 |
3 |

Sign in

4 |

Sign in or create an account for a best experience.

5 | Sign In | 6 | Create Account 7 |
8 | 9 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Home/_SignInInfo.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Claims; 2 | @{ 3 | var name = User.FindFirstValue("Name"); 4 | } 5 | 6 | 7 |
8 |
Welcome back, @name!
9 |

You have no unread notifications.

10 |
11 | My account 12 | @if (User.Identity.IsAuthenticated) 13 | { 14 | Sign out 15 | } 16 |
17 |
18 | 19 | -------------------------------------------------------------------------------- /src/Web/Views/Partials/Newsletter/_Newsletter.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

Newsletter

3 |
4 |

5 | Want to subscribe to our newsletter?
6 | List managed by our Newsletter Microservice. 7 |

8 |
9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 | We'll never share your email with anyone 😊. 18 |
19 | 20 |
21 |
22 |
23 |
24 |
Thanks for submitting!
25 | Click here to submit another email. 26 |
27 | 28 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Order/_CheckoutPreview.cshtml: -------------------------------------------------------------------------------- 1 | 
2 | 3 |
4 |

Account information

5 | 6 |
7 |
8 |

Payment Information

9 | 10 |
11 | 12 |
-------------------------------------------------------------------------------- /src/Web/Views/Partials/Recommendation/_Recommendation.cshtml: -------------------------------------------------------------------------------- 1 | @using Web.Models.Catalog; 2 | @model Product 3 |
4 |

Related products

5 | Recommendations provided by the Recommendation Microservice. 6 |
7 |
8 |
9 |
10 |
{{ p.name }}
11 | Placeholder 12 | View product 13 |
14 |
15 |
16 |
17 |

Sorry, no recommendations found yet for this product.

18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /src/Web/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

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

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

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

26 | -------------------------------------------------------------------------------- /src/Web/Views/Shared/_Spinner.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |
3 | Loading... 4 |
5 |
-------------------------------------------------------------------------------- /src/Web/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /src/Web/Views/Shared/_breadcrumbs.cshtml: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/Web/Views/Shared/_pagination.cshtml: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/Web/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Web 2 | @using Web.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /src/Web/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/Web/Web.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "Services": { 11 | "Catalog": "http://localhost:8001", 12 | "Newsletter": "http://localhost:8002", 13 | "Order": "http://localhost:8003", 14 | "Account": "http://localhost:8004", 15 | "Recommendation": "http://localhost:8005", 16 | "Notification": "http://localhost:8006", 17 | "Payment": "http://localhost:8007", 18 | "Shipping": "http://localhost:8008" 19 | }, 20 | "Redis": { 21 | "Configuration": "localhost", 22 | "InstanceName": "web" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "StoreSettings": { 10 | "Country": "United States", 11 | "Regions": [ "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DC", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY" ], 12 | "Currency": "USD", 13 | "CurrencyDisplay": "US$", 14 | "Tax": "0.05", 15 | "StoreName": "HildenCo WebStore", 16 | "StoreUrl": "https://www.hildenco.com" 17 | }, 18 | "Services": { 19 | "Catalog": "http://catalog", 20 | "Newsletter": "http://newsletter", 21 | "Order": "http://order", 22 | "Account": "http://account", 23 | "Recommendation": "http://recommendation", 24 | "Notification": "http://notification", 25 | "Payment": "http://payment", 26 | "Shipping": "http://shipping" 27 | }, 28 | "Redis": { 29 | "Configuration": "redis", 30 | "InstanceName": "web" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Web/build: -------------------------------------------------------------------------------- 1 | # to run this script run: 2 | # chmod +x ./build 3 | # so it can be executed on your WSL terminal 4 | docker.exe build -t web -q . 5 | -------------------------------------------------------------------------------- /src/Web/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd9/aspnet-microservices/fb64d38bc154acb19bed7dc5b29292192c4511f2/src/Web/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/Web/wwwroot/js/account.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd9/aspnet-microservices/fb64d38bc154acb19bed7dc5b29292192c4511f2/src/Web/wwwroot/js/account.min.js -------------------------------------------------------------------------------- /src/Web/wwwroot/js/catalog.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd9/aspnet-microservices/fb64d38bc154acb19bed7dc5b29292192c4511f2/src/Web/wwwroot/js/catalog.min.js -------------------------------------------------------------------------------- /src/Web/wwwroot/js/home.js: -------------------------------------------------------------------------------- 1 | // newsletter 2 | var nlApp = new Vue({ 3 | el: '#nlApp', 4 | data: { 5 | submitted: false, 6 | name: '', 7 | email: '' 8 | }, 9 | methods: { 10 | submit: function () { 11 | if (this.name.length < 5 || this.email.length < 5) { 12 | alert("Names and emails should have at least 5 characters"); 13 | return; 14 | } 15 | 16 | axios 17 | .post('/api/signup', { Name: this.name, Email: this.email }) 18 | .then(response => { 19 | this.submitted = true; 20 | }) 21 | .catch(error => console.log(error)); 22 | 23 | this.submitted = true; 24 | }, 25 | resubmit: function () { 26 | this.submitted = false; 27 | this.name = ''; 28 | this.email = ''; 29 | } 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /src/Web/wwwroot/js/home.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd9/aspnet-microservices/fb64d38bc154acb19bed7dc5b29292192c4511f2/src/Web/wwwroot/js/home.min.js -------------------------------------------------------------------------------- /src/Web/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/Web/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/Web/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /src/Web/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/grafana/provisioning/dashboards/dashboard.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'Prometheus' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: false 9 | editable: true 10 | allowUiUpdates: true 11 | options: 12 | path: /etc/grafana/provisioning/dashboards -------------------------------------------------------------------------------- /src/grafana/provisioning/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: Prometheus 5 | type: prometheus 6 | access: proxy 7 | orgId: 1 8 | url: http://prometheus:9090 9 | basicAuth: false 10 | isDefault: true 11 | editable: true -------------------------------------------------------------------------------- /src/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | scrape_configs: 2 | - job_name: cadvisor 3 | scrape_interval: 5s 4 | static_configs: 5 | - targets: 6 | - cadvisor:8080 7 | --------------------------------------------------------------------------------