Added signalr client for MVC Added signalr hub Added signalr events when order status changespull/702/merge
@ -0,0 +1,11 @@ | |||
{ | |||
"requires": true, | |||
"lockfileVersion": 1, | |||
"dependencies": { | |||
"@aspnet/signalr": { | |||
"version": "1.0.0-preview1-update1", | |||
"resolved": "https://registry.npmjs.org/@aspnet/signalr/-/signalr-1.0.0-preview1-update1.tgz", | |||
"integrity": "sha512-TGFCoLa2svN37Ew5ue0kGFxbkmQzq2I9N5TMLFBWRrpTYF4txlE3yPX0EaPt/NToKBaK1A+VK8Q+1MGSYtnqUw==" | |||
} | |||
} | |||
} |
@ -0,0 +1,50 @@ | |||
using MediatR; | |||
using Microsoft.AspNetCore.SignalR; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using Microsoft.Extensions.Logging; | |||
using Ordering.API.Infrastructure.Hubs; | |||
using Ordering.Domain.Events; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
namespace Ordering.API.Application.DomainEventHandlers.OrderCancelled | |||
{ | |||
public class OrderCancelledDomainEventHandler | |||
: INotificationHandler<OrderCancelledDomainEvent> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
private readonly IBuyerRepository _buyerRepository; | |||
private readonly ILoggerFactory _logger; | |||
private readonly IHubContext<NotificationsHub> _hubContext; | |||
public OrderCancelledDomainEventHandler( | |||
IOrderRepository orderRepository, | |||
ILoggerFactory logger, | |||
IBuyerRepository buyerRepository, | |||
IHubContext<NotificationsHub> hubContext) | |||
{ | |||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | |||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | |||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||
} | |||
public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken) | |||
{ | |||
_logger.CreateLogger(nameof(OrderCancelledDomainEvent)) | |||
.LogTrace($"Order with Id: {orderCancelledDomainEvent.Order.Id} has been successfully updated with " + | |||
$"a status order id: {OrderStatus.Shipped.Id}"); | |||
var order = await _orderRepository.GetAsync(orderCancelledDomainEvent.Order.Id); | |||
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); | |||
await _hubContext.Clients | |||
.Group(buyer.Name) | |||
.SendAsync("UpdatedOrderState", new { OrderId = order.Id, Status = order.OrderStatus.Name }); | |||
} | |||
} | |||
} |
@ -0,0 +1,50 @@ | |||
using MediatR; | |||
using Microsoft.AspNetCore.SignalR; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using Microsoft.Extensions.Logging; | |||
using Ordering.API.Infrastructure.Hubs; | |||
using Ordering.Domain.Events; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
namespace Ordering.API.Application.DomainEventHandlers.OrderShipped | |||
{ | |||
public class OrderShippedDomainEventHandler | |||
: INotificationHandler<OrderShippedDomainEvent> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
private readonly IBuyerRepository _buyerRepository; | |||
private readonly ILoggerFactory _logger; | |||
private readonly IHubContext<NotificationsHub> _hubContext; | |||
public OrderShippedDomainEventHandler( | |||
IOrderRepository orderRepository, | |||
ILoggerFactory logger, | |||
IBuyerRepository buyerRepository, | |||
IHubContext<NotificationsHub> hubContext) | |||
{ | |||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | |||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | |||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||
} | |||
public async Task Handle(OrderShippedDomainEvent orderShippedDomainEvent, CancellationToken cancellationToken) | |||
{ | |||
_logger.CreateLogger(nameof(OrderShippedDomainEvent)) | |||
.LogTrace($"Order with Id: {orderShippedDomainEvent.Order.Id} has been successfully updated with " + | |||
$"a status order id: {OrderStatus.Shipped.Id}"); | |||
var order = await _orderRepository.GetAsync(orderShippedDomainEvent.Order.Id); | |||
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); | |||
await _hubContext.Clients | |||
.Group(buyer.Name) | |||
.SendAsync("UpdatedOrderState", new { OrderId = order.Id, Status = order.OrderStatus.Name }); | |||
} | |||
} | |||
} |
@ -0,0 +1,27 @@ | |||
using Microsoft.AspNetCore.Authorization; | |||
using Microsoft.AspNetCore.SignalR; | |||
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Ordering.API.Infrastructure.Hubs | |||
{ | |||
[Authorize] | |||
public class NotificationsHub : Hub | |||
{ | |||
public override async Task OnConnectedAsync() | |||
{ | |||
await Groups.AddAsync(Context.ConnectionId, Context.User.Identity.Name); | |||
await base.OnConnectedAsync(); | |||
} | |||
public override async Task OnDisconnectedAsync(Exception ex) | |||
{ | |||
await Groups.RemoveAsync(Context.ConnectionId, Context.User.Identity.Name); | |||
await base.OnDisconnectedAsync(ex); | |||
} | |||
} | |||
} |
@ -0,0 +1,238 @@ | |||
// <auto-generated /> | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Metadata; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using Microsoft.EntityFrameworkCore.Storage; | |||
using Microsoft.EntityFrameworkCore.Storage.Internal; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; | |||
using System; | |||
namespace Ordering.API.Infrastructure.Migrations | |||
{ | |||
[DbContext(typeof(OrderingContext))] | |||
[Migration("20180412143935_NamePropertyInBuyer")] | |||
partial class NamePropertyInBuyer | |||
{ | |||
protected override void BuildTargetModel(ModelBuilder modelBuilder) | |||
{ | |||
#pragma warning disable 612, 618 | |||
modelBuilder | |||
.HasAnnotation("ProductVersion", "2.0.1-rtm-125") | |||
.HasAnnotation("Relational:Sequence:.orderitemseq", "'orderitemseq', '', '1', '10', '', '', 'Int64', 'False'") | |||
.HasAnnotation("Relational:Sequence:ordering.buyerseq", "'buyerseq', 'ordering', '1', '10', '', '', 'Int64', 'False'") | |||
.HasAnnotation("Relational:Sequence:ordering.orderseq", "'orderseq', 'ordering', '1', '10', '', '', 'Int64', 'False'") | |||
.HasAnnotation("Relational:Sequence:ordering.paymentseq", "'paymentseq', 'ordering', '1', '10', '', '', 'Int64', 'False'") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd() | |||
.HasAnnotation("SqlServer:HiLoSequenceName", "buyerseq") | |||
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); | |||
b.Property<string>("IdentityGuid") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<string>("Name"); | |||
b.HasKey("Id"); | |||
b.HasIndex("IdentityGuid") | |||
.IsUnique(); | |||
b.ToTable("buyers","ordering"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.CardType", b => | |||
{ | |||
b.Property<int>("Id") | |||
.HasDefaultValue(1); | |||
b.Property<string>("Name") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.ToTable("cardtypes","ordering"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd() | |||
.HasAnnotation("SqlServer:HiLoSequenceName", "paymentseq") | |||
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); | |||
b.Property<string>("Alias") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<int>("BuyerId"); | |||
b.Property<string>("CardHolderName") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<string>("CardNumber") | |||
.IsRequired() | |||
.HasMaxLength(25); | |||
b.Property<int>("CardTypeId"); | |||
b.Property<DateTime>("Expiration"); | |||
b.HasKey("Id"); | |||
b.HasIndex("BuyerId"); | |||
b.HasIndex("CardTypeId"); | |||
b.ToTable("paymentmethods","ordering"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd() | |||
.HasAnnotation("SqlServer:HiLoSequenceName", "orderseq") | |||
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); | |||
b.Property<int?>("BuyerId"); | |||
b.Property<string>("Description"); | |||
b.Property<DateTime>("OrderDate"); | |||
b.Property<int>("OrderStatusId"); | |||
b.Property<int?>("PaymentMethodId"); | |||
b.HasKey("Id"); | |||
b.HasIndex("BuyerId"); | |||
b.HasIndex("OrderStatusId"); | |||
b.HasIndex("PaymentMethodId"); | |||
b.ToTable("orders","ordering"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderItem", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd() | |||
.HasAnnotation("SqlServer:HiLoSequenceName", "orderitemseq") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); | |||
b.Property<decimal>("Discount"); | |||
b.Property<int>("OrderId"); | |||
b.Property<string>("PictureUrl"); | |||
b.Property<int>("ProductId"); | |||
b.Property<string>("ProductName") | |||
.IsRequired(); | |||
b.Property<decimal>("UnitPrice"); | |||
b.Property<int>("Units"); | |||
b.HasKey("Id"); | |||
b.HasIndex("OrderId"); | |||
b.ToTable("orderItems","ordering"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", b => | |||
{ | |||
b.Property<int>("Id") | |||
.HasDefaultValue(1); | |||
b.Property<string>("Name") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.ToTable("orderstatus","ordering"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency.ClientRequest", b => | |||
{ | |||
b.Property<Guid>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<string>("Name") | |||
.IsRequired(); | |||
b.Property<DateTime>("Time"); | |||
b.HasKey("Id"); | |||
b.ToTable("requests","ordering"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b => | |||
{ | |||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer") | |||
.WithMany("PaymentMethods") | |||
.HasForeignKey("BuyerId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.CardType", "CardType") | |||
.WithMany() | |||
.HasForeignKey("CardTypeId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b => | |||
{ | |||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer") | |||
.WithMany() | |||
.HasForeignKey("BuyerId"); | |||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", "OrderStatus") | |||
.WithMany() | |||
.HasForeignKey("OrderStatusId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod") | |||
.WithMany() | |||
.HasForeignKey("PaymentMethodId") | |||
.OnDelete(DeleteBehavior.Restrict); | |||
b.OwnsOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", "Address", b1 => | |||
{ | |||
b1.Property<int>("OrderId"); | |||
b1.ToTable("orders","ordering"); | |||
b1.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order") | |||
.WithOne("Address") | |||
.HasForeignKey("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", "OrderId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderItem", b => | |||
{ | |||
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order") | |||
.WithMany("OrderItems") | |||
.HasForeignKey("OrderId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
#pragma warning restore 612, 618 | |||
} | |||
} | |||
} |
@ -0,0 +1,86 @@ | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using System; | |||
using System.Collections.Generic; | |||
namespace Ordering.API.Infrastructure.Migrations | |||
{ | |||
public partial class NamePropertyInBuyer : Migration | |||
{ | |||
protected override void Up(MigrationBuilder migrationBuilder) | |||
{ | |||
migrationBuilder.DropForeignKey( | |||
name: "FK_orderItems_orders_OrderId", | |||
schema: "ordering", | |||
table: "orderItems"); | |||
migrationBuilder.AddColumn<string>( | |||
name: "Name", | |||
schema: "ordering", | |||
table: "buyers", | |||
nullable: true); | |||
migrationBuilder.AddForeignKey( | |||
name: "FK_orderItems_orders_OrderId", | |||
schema: "ordering", | |||
table: "orderItems", | |||
column: "OrderId", | |||
principalSchema: "ordering", | |||
principalTable: "orders", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
} | |||
protected override void Down(MigrationBuilder migrationBuilder) | |||
{ | |||
migrationBuilder.DropForeignKey( | |||
name: "FK_orderItems_orders_OrderId", | |||
schema: "ordering", | |||
table: "orderItems"); | |||
migrationBuilder.DropColumn( | |||
name: "Name", | |||
schema: "ordering", | |||
table: "buyers"); | |||
migrationBuilder.AddColumn<string>( | |||
name: "City", | |||
schema: "ordering", | |||
table: "orders", | |||
nullable: true); | |||
migrationBuilder.AddColumn<string>( | |||
name: "Country", | |||
schema: "ordering", | |||
table: "orders", | |||
nullable: true); | |||
migrationBuilder.AddColumn<string>( | |||
name: "State", | |||
schema: "ordering", | |||
table: "orders", | |||
nullable: true); | |||
migrationBuilder.AddColumn<string>( | |||
name: "Street", | |||
schema: "ordering", | |||
table: "orders", | |||
nullable: true); | |||
migrationBuilder.AddColumn<string>( | |||
name: "ZipCode", | |||
schema: "ordering", | |||
table: "orders", | |||
nullable: true); | |||
migrationBuilder.AddForeignKey( | |||
name: "FK_orderItems_orders_OrderId", | |||
schema: "ordering", | |||
table: "orderItems", | |||
column: "OrderId", | |||
principalSchema: "ordering", | |||
principalTable: "orders", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
} | |||
} | |||
} |
@ -0,0 +1,18 @@ | |||
using MediatR; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace Ordering.Domain.Events | |||
{ | |||
public class OrderCancelledDomainEvent : INotification | |||
{ | |||
public Order Order { get; } | |||
public OrderCancelledDomainEvent(Order order) | |||
{ | |||
Order = order; | |||
} | |||
} | |||
} |
@ -0,0 +1,15 @@ | |||
using MediatR; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
namespace Ordering.Domain.Events | |||
{ | |||
public class OrderShippedDomainEvent : INotification | |||
{ | |||
public Order Order { get; } | |||
public OrderShippedDomainEvent(Order order) | |||
{ | |||
Order = order; | |||
} | |||
} | |||
} |
@ -0,0 +1,26 @@ | |||
{ | |||
"name": "asp.net", | |||
"version": "1.0.0", | |||
"lockfileVersion": 1, | |||
"requires": true, | |||
"dependencies": { | |||
"@aspnet/signalr": { | |||
"version": "1.0.0-preview2-final", | |||
"resolved": "https://registry.npmjs.org/@aspnet/signalr/-/signalr-1.0.0-preview2-final.tgz", | |||
"integrity": "sha512-XbqGbAG9Ow4L5Sc4n81A2S8lHSlxBNTjFm3WZQA94cIolPnW0bPK2u14UMooXRXxzjBtJViJMN/aoxWRwTWxig==" | |||
}, | |||
"jquery": { | |||
"version": "3.3.1", | |||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", | |||
"integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" | |||
}, | |||
"toastr": { | |||
"version": "2.1.4", | |||
"resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz", | |||
"integrity": "sha1-i0O+ZPudDEFIcURvLbjoyk6V8YE=", | |||
"requires": { | |||
"jquery": "3.3.1" | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,10 @@ | |||
{ | |||
"version": "1.0.0", | |||
"name": "asp.net", | |||
"private": true, | |||
"devDependencies": {}, | |||
"dependencies": { | |||
"@aspnet/signalr": "^1.0.0-preview2-final", | |||
"toastr": "^2.1.4" | |||
} | |||
} |