Merge pull request #9 from dotnet-architecture/dev
PR to merge into skynode dev branch
This commit is contained in:
commit
52e0a4e3b3
@ -117,6 +117,7 @@ services:
|
||||
- "6379:6379"
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq
|
||||
image: rabbitmq:3-management
|
||||
ports:
|
||||
- "15672:15672"
|
||||
- "5672:5672"
|
@ -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);
|
||||
|
||||
|
@ -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<HttpGlobalExceptionFilter> 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
namespace Basket.API.Infrastructure.Filters
|
||||
{
|
||||
public class JsonErrorResponse
|
||||
{
|
||||
public string[] Messages { get; set; }
|
||||
|
||||
public object DeveloperMessage { get; set; }
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
var results = new List<ValidationResult>();
|
||||
|
||||
if (Quantity < 1)
|
||||
{
|
||||
results.Add(new ValidationResult("Invalid number of units", new []{ "Quantity" }));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 =>
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -2,6 +2,12 @@
|
||||
<esh-header url="/catalog">Back to catalog</esh-header>
|
||||
|
||||
<div class="container">
|
||||
<div *ngFor="let errorMessage of errorMessages">
|
||||
<div class="esh-basket-items-margin-left1 row">
|
||||
<div class="alert alert-warning" role="alert"> {{errorMessage}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<article class="esh-basket-titles row">
|
||||
<section class="esh-basket-title col-xs-3">Product</section>
|
||||
<section class="esh-basket-title col-xs-3 hidden-lg-down"></section>
|
||||
@ -19,7 +25,8 @@
|
||||
<section class="esh-basket-item esh-basket-item--middle col-xs-3">{{item.productName}}</section>
|
||||
<section class="esh-basket-item esh-basket-item--middle col-xs-2">$ {{item.unitPrice | number:'.2-2'}}</section>
|
||||
<section class="esh-basket-item esh-basket-item--middle col-xs-2">
|
||||
<input class="esh-basket-input"
|
||||
<input id="quantity"
|
||||
class="esh-basket-input"
|
||||
type="number"
|
||||
min="1"
|
||||
[(ngModel)]="item.quantity"
|
||||
|
@ -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;
|
||||
|
||||
@ -29,13 +34,27 @@ export class BasketComponent implements OnInit {
|
||||
this.service.setBasket(this.basket).subscribe(x => console.log('basket updated: ' + x));
|
||||
}
|
||||
|
||||
update(event: any) {
|
||||
this.service.setBasket(this.basket).subscribe(x => console.log('basket updated: ' + x));
|
||||
update(event: any): Observable<boolean> {
|
||||
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.update(event)
|
||||
.subscribe(
|
||||
x => {
|
||||
this.errorMessages = [];
|
||||
this.basketwrapper.basket = this.basket;
|
||||
this.router.navigate(['order']);
|
||||
this.router.navigate(['order'],
|
||||
errMessage => this.errorMessages = errMessage.messages);
|
||||
});
|
||||
}
|
||||
|
||||
private calculateTotalPrice() {
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -5,6 +5,7 @@ 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 { 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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
47
test/Services/UnitTest/Ordering/Builders.cs
Normal file
47
test/Services/UnitTest/Ordering/Builders.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<OrderingDomainException>(() => 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()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user