From 3d552b3219edf981f7ba0ffdcf504749e2877698 Mon Sep 17 00:00:00 2001 From: "PLAINCONCEPTS\\lruiz" Date: Tue, 22 Aug 2017 12:04:40 +0200 Subject: [PATCH 1/6] Fix bug #263 --- .../AggregatesModel/OrderAggregate/Order.cs | 8 +++- .../OrderAggregate/OrderItem.cs | 5 ++ test/Services/UnitTest/Ordering/Builders.cs | 47 +++++++++++++++++++ .../Ordering/Domain/OrderAggregateTest.cs | 14 ++++++ 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 test/Services/UnitTest/Ordering/Builders.cs diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs index 37c17c897..ae2194948 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs @@ -72,8 +72,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O if (discount > existingOrderForProduct.GetCurrentDiscount()) { existingOrderForProduct.SetNewDiscount(discount); - existingOrderForProduct.AddUnits(units); } + + existingOrderForProduct.AddUnits(units); } else { @@ -187,6 +188,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O { throw new OrderingDomainException($"Not possible to change order status from {OrderStatus.Name} to {orderStatusToChange.Name}."); } + + public decimal GetTotal() + { + return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice()); + } } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs index fd65fb3ca..1b7896dcc 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs @@ -59,6 +59,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O return _units; } + public decimal GetUnitPrice() + { + return _unitPrice; + } + public string GetOrderItemProductName() => _productName; public void SetNewDiscount(decimal discount) diff --git a/test/Services/UnitTest/Ordering/Builders.cs b/test/Services/UnitTest/Ordering/Builders.cs new file mode 100644 index 000000000..c9dd9d366 --- /dev/null +++ b/test/Services/UnitTest/Ordering/Builders.cs @@ -0,0 +1,47 @@ +using System; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + +namespace UnitTest.Ordering +{ + public class AddressBuilder + { + public Address Build() + { + return new Address("street", "city", "state", "country", "zipcode"); + } + } + + public class OrderBuilder + { + private readonly Order order; + + public OrderBuilder(Address address) + { + order = new Order( + "userId", + address, + cardTypeId:5, + cardNumber:"12", + cardSecurityNumber:"123", + cardHolderName:"name", + cardExpiration:DateTime.UtcNow); + } + + public OrderBuilder AddOne( + int productId, + string productName, + decimal unitPrice, + decimal discount, + string pictureUrl, + int units = 1) + { + order.AddOrderItem(productId, productName, unitPrice, discount, pictureUrl, units); + return this; + } + + public Order Build() + { + return order; + } + } +} diff --git a/test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs b/test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs index dfa31eb62..eaa0d0cb6 100644 --- a/test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs +++ b/test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs @@ -2,6 +2,8 @@ using Ordering.Domain.Events; using Ordering.Domain.Exceptions; using System; +using System.Linq; +using UnitTest.Ordering; using Xunit; public class OrderAggregateTest @@ -93,6 +95,18 @@ public class OrderAggregateTest Assert.Throws(() => fakeOrderItem.AddUnits(-1)); } + [Fact] + public void when_add_two_times_on_the_same_item_then_the_total_of_order_should_be_the_sum_of_the_two_items() + { + var address = new AddressBuilder().Build(); + var order = new OrderBuilder(address) + .AddOne(1,"cup",10.0m,0,string.Empty) + .AddOne(1,"cup",10.0m,0,string.Empty) + .Build(); + + Assert.Equal(20.0m, order.GetTotal()); + } + [Fact] public void Add_new_Order_raises_new_event() { From 737bddadc2eda6af52a3dd65d488c248826fa7ee Mon Sep 17 00:00:00 2001 From: "PLAINCONCEPTS\\lruiz" Date: Tue, 22 Aug 2017 18:03:12 +0200 Subject: [PATCH 2/6] Fix issue #259 --- .../Filters/HttpGlobalExceptionFilter.cs | 9 +----- .../Filters/JsonErrorResponse.cs | 9 ++++++ .../Filters/ValidateModelStateFilter.cs | 30 +++++++++++++++++++ .../Basket/Basket.API/Model/BasketItem.cs | 18 +++++++++-- src/Services/Basket/Basket.API/Startup.cs | 1 + .../modules/basket/basket.component.html | 12 ++++++-- .../Client/modules/basket/basket.component.ts | 10 ++++++- .../Client/modules/orders/orders.component.ts | 4 +-- .../modules/shared/services/data.service.ts | 2 +- 9 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs create mode 100644 src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index ab7989973..dab725915 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -8,7 +8,7 @@ using System.Net; namespace Basket.API.Infrastructure.Filters { - public class HttpGlobalExceptionFilter : IExceptionFilter + public partial class HttpGlobalExceptionFilter : IExceptionFilter { private readonly IHostingEnvironment env; private readonly ILogger logger; @@ -52,12 +52,5 @@ namespace Basket.API.Infrastructure.Filters } context.ExceptionHandled = true; } - - private class JsonErrorResponse - { - public string[] Messages { get; set; } - - public object DeveloperMessage { get; set; } - } } } diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs new file mode 100644 index 000000000..bcadc7358 --- /dev/null +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs @@ -0,0 +1,9 @@ +namespace Basket.API.Infrastructure.Filters +{ + public class JsonErrorResponse + { + public string[] Messages { get; set; } + + public object DeveloperMessage { get; set; } + } +} diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs new file mode 100644 index 000000000..8ef72edb6 --- /dev/null +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs @@ -0,0 +1,30 @@ +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Basket.API.Infrastructure.Filters +{ + public class ValidateModelStateFilter : ActionFilterAttribute + { + public override void OnActionExecuting(ActionExecutingContext context) + { + if (context.ModelState.IsValid) + { + return; + } + + var validationErrors = context.ModelState + .Keys + .SelectMany(k => context.ModelState[k].Errors) + .Select(e => e.ErrorMessage) + .ToArray(); + + var json = new JsonErrorResponse + { + Messages = validationErrors + }; + + context.Result = new BadRequestObjectResult(json); + } + } +} diff --git a/src/Services/Basket/Basket.API/Model/BasketItem.cs b/src/Services/Basket/Basket.API/Model/BasketItem.cs index 8b98befcc..a41945145 100644 --- a/src/Services/Basket/Basket.API/Model/BasketItem.cs +++ b/src/Services/Basket/Basket.API/Model/BasketItem.cs @@ -1,6 +1,9 @@ -namespace Microsoft.eShopOnContainers.Services.Basket.API.Model +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace Microsoft.eShopOnContainers.Services.Basket.API.Model { - public class BasketItem + public class BasketItem : IValidatableObject { public string Id { get; set; } public string ProductId { get; set; } @@ -9,5 +12,16 @@ public decimal OldUnitPrice { get; set; } public int Quantity { get; set; } public string PictureUrl { get; set; } + public IEnumerable Validate(ValidationContext validationContext) + { + var results = new List(); + + if (Quantity < 1) + { + results.Add(new ValidationResult("Invalid number of units", new []{ "Quantity" })); + } + + return results; + } } } diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index a473a29fc..18e21e209 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -54,6 +54,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API services.AddMvc(options => { options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + options.Filters.Add(typeof(ValidateModelStateFilter)); }).AddControllersAsServices(); services.AddHealthChecks(checks => diff --git a/src/Web/WebSPA/Client/modules/basket/basket.component.html b/src/Web/WebSPA/Client/modules/basket/basket.component.html index 6787bc0cb..e700f622d 100644 --- a/src/Web/WebSPA/Client/modules/basket/basket.component.html +++ b/src/Web/WebSPA/Client/modules/basket/basket.component.html @@ -1,7 +1,13 @@
Back to catalog - +
+
+
+ +
+
+
Product
@@ -14,7 +20,7 @@
- +
{{item.productName}}
$ {{item.unitPrice | number:'.2-2'}}
@@ -23,7 +29,7 @@ type="number" min="1" [(ngModel)]="item.quantity" - (change)="itemQuantityChanged(item)" /> + (change)="itemQuantityChanged(item)"/>
$ {{(item.unitPrice * item.quantity) | number:'.2-2'}}
diff --git a/src/Web/WebSPA/Client/modules/basket/basket.component.ts b/src/Web/WebSPA/Client/modules/basket/basket.component.ts index 4637e93fa..879ef784b 100644 --- a/src/Web/WebSPA/Client/modules/basket/basket.component.ts +++ b/src/Web/WebSPA/Client/modules/basket/basket.component.ts @@ -1,6 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; +import 'rxjs/Rx'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/observable/throw'; + import { BasketService } from './basket.service'; import { IBasket } from '../shared/models/basket.model'; import { IBasketItem } from '../shared/models/basketItem.model'; @@ -12,6 +16,7 @@ import { BasketWrapperService } from '../shared/services/basket.wrapper.service' templateUrl: './basket.component.html' }) export class BasketComponent implements OnInit { + errorMessages: any; basket: IBasket; totalPrice: number = 0; @@ -30,7 +35,10 @@ export class BasketComponent implements OnInit { } update(event: any) { - this.service.setBasket(this.basket).subscribe(x => console.log('basket updated: ' + x)); + this.service.setBasket(this.basket).catch((errMessage) => { + this.errorMessages = errMessage.messages; + return Observable.throw(errMessage); + }).subscribe(x => console.log('basket updated: ' + x)); } checkOut(event: any) { diff --git a/src/Web/WebSPA/Client/modules/orders/orders.component.ts b/src/Web/WebSPA/Client/modules/orders/orders.component.ts index 105965200..a6af462ac 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders.component.ts +++ b/src/Web/WebSPA/Client/modules/orders/orders.component.ts @@ -20,7 +20,7 @@ export class OrdersComponent implements OnInit { ngOnInit() { if (this.configurationService.isReady) { - this.getOrders() + this.getOrders(); } else { this.configurationService.settingsLoaded$.subscribe(x => { this.getOrders(); @@ -31,7 +31,7 @@ export class OrdersComponent implements OnInit { this.interval = setTimeout(() => { this.service.getOrders().subscribe(orders => { this.orders = orders; - if (this.orders.length != this.oldOrders.length) { + if (this.orders.length !== this.oldOrders.length) { clearInterval(this.interval); } }); diff --git a/src/Web/WebSPA/Client/modules/shared/services/data.service.ts b/src/Web/WebSPA/Client/modules/shared/services/data.service.ts index 64288e127..26f7801ca 100644 --- a/src/Web/WebSPA/Client/modules/shared/services/data.service.ts +++ b/src/Web/WebSPA/Client/modules/shared/services/data.service.ts @@ -102,7 +102,7 @@ export class DataService { if (error instanceof Response) { let errMessage = ''; try { - errMessage = error.json().error; + errMessage = error.json(); } catch (err) { errMessage = error.statusText; } From a6acdabf850dd0aa198ac63f1245fd80f8d53b0b Mon Sep 17 00:00:00 2001 From: "PLAINCONCEPTS\\lruiz" Date: Tue, 22 Aug 2017 20:03:48 +0200 Subject: [PATCH 3/6] Fix bug #283 --- .../Client/modules/shared/services/basket.wrapper.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Web/WebSPA/Client/modules/shared/services/basket.wrapper.service.ts b/src/Web/WebSPA/Client/modules/shared/services/basket.wrapper.service.ts index 3017a30f3..447fd9441 100644 --- a/src/Web/WebSPA/Client/modules/shared/services/basket.wrapper.service.ts +++ b/src/Web/WebSPA/Client/modules/shared/services/basket.wrapper.service.ts @@ -4,7 +4,8 @@ import { Subject } from 'rxjs/Subject'; import { ICatalogItem } from '../models/catalogItem.model'; import { IBasketItem } from '../models/basketItem.model'; import { IBasket } from '../models/basket.model'; -import { SecurityService } from '../services/security.service'; +import { SecurityService } from '../services/security.service'; +import { Guid } from '../../../guid'; @Injectable() export class BasketWrapperService { @@ -27,7 +28,7 @@ export class BasketWrapperService { productName: item.name, quantity: 1, unitPrice: item.price, - id: '', + id: Guid.newGuid(), oldUnitPrice: 0 }; From 8d14869ca0479907db89d11446a3f8030f379164 Mon Sep 17 00:00:00 2001 From: "PLAINCONCEPTS\\lruiz" Date: Wed, 23 Aug 2017 11:06:50 +0200 Subject: [PATCH 4/6] Fix bug #259 --- .../modules/basket/basket.component.html | 5 +-- .../Client/modules/basket/basket.component.ts | 35 ++++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/Web/WebSPA/Client/modules/basket/basket.component.html b/src/Web/WebSPA/Client/modules/basket/basket.component.html index e700f622d..52aab2fe3 100644 --- a/src/Web/WebSPA/Client/modules/basket/basket.component.html +++ b/src/Web/WebSPA/Client/modules/basket/basket.component.html @@ -7,7 +7,7 @@
- +
Product
@@ -25,7 +25,8 @@
{{item.productName}}
$ {{item.unitPrice | number:'.2-2'}}
- console.log('basket updated: ' + x)); } - update(event: any) { - this.service.setBasket(this.basket).catch((errMessage) => { - this.errorMessages = errMessage.messages; - return Observable.throw(errMessage); - }).subscribe(x => console.log('basket updated: ' + x)); + update(event: any): Observable { + let setBasketObservable = this.service.setBasket(this.basket); + setBasketObservable + .subscribe( + x => { + this.errorMessages = []; + console.log('basket updated: ' + x); + }, + errMessage => this.errorMessages = errMessage.messages); + return setBasketObservable; } checkOut(event: any) { - this.basketwrapper.basket = this.basket; - this.router.navigate(['order']); + this.update(event) + .subscribe( + x => { + this.errorMessages = []; + this.basketwrapper.basket = this.basket; + this.router.navigate(['order'], + errMessage => this.errorMessages = errMessage.messages); + }); } private calculateTotalPrice() { From 5e1d3437591b78a821c6f4dfa52bd18dc2e90240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Wed, 23 Aug 2017 14:42:01 +0200 Subject: [PATCH 5/6] Added enhancement rabbitmq management console #246 --- docker-compose.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 253674b57..86931228e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -115,8 +115,9 @@ services: image: redis ports: - "6379:6379" - + rabbitmq: - image: rabbitmq + image: rabbitmq:3-management ports: + - "15672:15672" - "5672:5672" \ No newline at end of file From 3928ead483ff85bc62f7a9148f43d0d8b0fdf2bf Mon Sep 17 00:00:00 2001 From: Polys Georgiou Date: Wed, 23 Aug 2017 17:38:19 +0100 Subject: [PATCH 6/6] Fix queue/table name null or whitespace check --- .../AzureHealthCheckBuilderExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/AzureHealthCheckBuilderExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/AzureHealthCheckBuilderExtensions.cs index 4c917f223..5a06a3ba2 100644 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/AzureHealthCheckBuilderExtensions.cs +++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/AzureHealthCheckBuilderExtensions.cs @@ -70,7 +70,7 @@ namespace Microsoft.Extensions.HealthChecks var properties = await tableClient.GetServicePropertiesAsync().ConfigureAwait(false); - if (String.IsNullOrWhiteSpace(tableName)) + if (!String.IsNullOrWhiteSpace(tableName)) { var table = tableClient.GetTableReference(tableName); @@ -150,7 +150,7 @@ namespace Microsoft.Extensions.HealthChecks var properties = await queueClient.GetServicePropertiesAsync().ConfigureAwait(false); - if (String.IsNullOrWhiteSpace(queueName)) + if (!String.IsNullOrWhiteSpace(queueName)) { var queue = queueClient.GetQueueReference(queueName);