@ -1,19 +0,0 @@ | |||||
version: '3.4' | |||||
services: | |||||
ci-build: | |||||
image: microsoft/aspnetcore-build:2.0.2 #Depending on the bug below, you can also try this other SDK image: microsoft/aspnetcore-build:1.0-2.0-2017-10 or microsoft/aspnetcore-build:1.0-2.0 | |||||
volumes: | |||||
- .:/src | |||||
- ./cli-linux:/cli-linux | |||||
working_dir: /src | |||||
# Next line is using the .sln file to compile all the projects. | |||||
# Sometime there is an issue in msbuild exits the process before finishing building the bits: (https://github.com/Microsoft/msbuild/issues/2153) | |||||
# Random error: error MSB4017: The build stopped unexpectedly be cause of an unexpected logger failure. | |||||
command: /bin/bash -c "pushd ./src/Web/WebSPA && npm rebuild node-sass && popd && dotnet publish ./eShopOnContainers-ServicesAndWebApps.sln -c Release -o ./obj/Docker/publish" | |||||
# NOTE: Using build-bits-linux.sh from Linux build container exits before ending. | |||||
#command: /bin/bash -c "pushd ./src/Web/WebSPA && npm rebuild node-sass && popd && pushd /cli-linux && ./build-bits-linux.sh /src" | |||||
@ -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,46 @@ | |||||
using MediatR; | |||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | |||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||||
using Microsoft.Extensions.Logging; | |||||
using Ordering.API.Application.IntegrationEvents; | |||||
using Ordering.API.Application.IntegrationEvents.Events; | |||||
using Ordering.Domain.Events; | |||||
using System; | |||||
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 IOrderingIntegrationEventService _orderingIntegrationEventService; | |||||
public OrderCancelledDomainEventHandler( | |||||
IOrderRepository orderRepository, | |||||
ILoggerFactory logger, | |||||
IBuyerRepository buyerRepository, | |||||
IOrderingIntegrationEventService orderingIntegrationEventService) | |||||
{ | |||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | |||||
} | |||||
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()); | |||||
var orderStatusChangedToCancelledIntegrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); | |||||
await _orderingIntegrationEventService.PublishThroughEventBusAsync(orderStatusChangedToCancelledIntegrationEvent); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,47 @@ | |||||
using MediatR; | |||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | |||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||||
using Microsoft.Extensions.Logging; | |||||
using Ordering.API.Application.IntegrationEvents; | |||||
using Ordering.API.Application.IntegrationEvents.Events; | |||||
using Ordering.Domain.Events; | |||||
using System; | |||||
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 IOrderingIntegrationEventService _orderingIntegrationEventService; | |||||
private readonly ILoggerFactory _logger; | |||||
public OrderShippedDomainEventHandler( | |||||
IOrderRepository orderRepository, | |||||
ILoggerFactory logger, | |||||
IBuyerRepository buyerRepository, | |||||
IOrderingIntegrationEventService orderingIntegrationEventService) | |||||
{ | |||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | |||||
_orderingIntegrationEventService = orderingIntegrationEventService; | |||||
} | |||||
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()); | |||||
var orderStatusChangedToShippedIntegrationEvent = new OrderStatusChangedToShippedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); | |||||
await _orderingIntegrationEventService.PublishThroughEventBusAsync(orderStatusChangedToShippedIntegrationEvent); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.API.Application.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.API.Application.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.API.Application.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -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,30 @@ | |||||
using Autofac; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Ordering.SignalrHub.IntegrationEvents; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Reflection; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.AutofacModules | |||||
{ | |||||
public class ApplicationModule | |||||
: Autofac.Module | |||||
{ | |||||
public string QueriesConnectionString { get; } | |||||
public ApplicationModule() | |||||
{ | |||||
} | |||||
protected override void Load(ContainerBuilder builder) | |||||
{ | |||||
builder.RegisterAssemblyTypes(typeof(OrderStatusChangedToAwaitingValidationIntegrationEvent).GetTypeInfo().Assembly) | |||||
.AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,20 @@ | |||||
FROM microsoft/aspnetcore:2.0 AS base | |||||
WORKDIR /app | |||||
EXPOSE 80 | |||||
FROM microsoft/aspnetcore-build:2.0 AS build | |||||
WORKDIR /src | |||||
COPY eShopOnContainers-ServicesAndWebApps.sln ./ | |||||
COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/ | |||||
RUN dotnet restore -nowarn:msb3202,nu1503 | |||||
COPY . . | |||||
WORKDIR /src/src/Services/Ordering/Ordering.SignalrHub | |||||
RUN dotnet build Ordering.SignalrHub.csproj -c Release -o /app | |||||
FROM build AS publish | |||||
RUN dotnet publish Ordering.SignalrHub.csproj -c Release -o /app | |||||
FROM base AS final | |||||
WORKDIR /app | |||||
COPY --from=publish /app . | |||||
ENTRYPOINT ["dotnet", "Ordering.SignalrHub.dll"] |
@ -0,0 +1,28 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Ordering.SignalrHub.IntegrationEvents.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.EventHandling | |||||
{ | |||||
public class OrderStatusChangedToCancelledIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToCancelledIntegrationEvent> | |||||
{ | |||||
private readonly IHubContext<NotificationsHub> _hubContext; | |||||
public OrderStatusChangedToCancelledIntegrationEventHandler(IHubContext<NotificationsHub> hubContext) | |||||
{ | |||||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToCancelledIntegrationEvent @event) | |||||
{ | |||||
await _hubContext.Clients | |||||
.Group(@event.BuyerName) | |||||
.SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,26 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Ordering.SignalrHub.IntegrationEvents.Events; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.EventHandling | |||||
{ | |||||
public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToPaidIntegrationEvent> | |||||
{ | |||||
private readonly IHubContext<NotificationsHub> _hubContext; | |||||
public OrderStatusChangedToPaidIntegrationEventHandler(IHubContext<NotificationsHub> hubContext) | |||||
{ | |||||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) | |||||
{ | |||||
await _hubContext.Clients | |||||
.Group(@event.BuyerName) | |||||
.SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,28 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Ordering.SignalrHub.IntegrationEvents.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.EventHandling | |||||
{ | |||||
public class OrderStatusChangedToShippedIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToShippedIntegrationEvent> | |||||
{ | |||||
private readonly IHubContext<NotificationsHub> _hubContext; | |||||
public OrderStatusChangedToShippedIntegrationEventHandler(IHubContext<NotificationsHub> hubContext) | |||||
{ | |||||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToShippedIntegrationEvent @event) | |||||
{ | |||||
await _hubContext.Clients | |||||
.Group(@event.BuyerName) | |||||
.SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,29 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Ordering.SignalrHub.IntegrationEvents.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.EventHandling | |||||
{ | |||||
public class OrderStatusChangedToStockConfirmedIntegrationEventHandler : | |||||
IIntegrationEventHandler<OrderStatusChangedToStockConfirmedIntegrationEvent> | |||||
{ | |||||
private readonly IHubContext<NotificationsHub> _hubContext; | |||||
public OrderStatusChangedToStockConfirmedIntegrationEventHandler(IHubContext<NotificationsHub> hubContext) | |||||
{ | |||||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToStockConfirmedIntegrationEvent @event) | |||||
{ | |||||
await _hubContext.Clients | |||||
.Group(@event.BuyerName) | |||||
.SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,29 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Ordering.SignalrHub.IntegrationEvents.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.EventHandling | |||||
{ | |||||
public class OrderStatusChangedToSubmittedIntegrationEventHandler : | |||||
IIntegrationEventHandler<OrderStatusChangedToSubmittedIntegrationEvent> | |||||
{ | |||||
private readonly IHubContext<NotificationsHub> _hubContext; | |||||
public OrderStatusChangedToSubmittedIntegrationEventHandler(IHubContext<NotificationsHub> hubContext) | |||||
{ | |||||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToSubmittedIntegrationEvent @event) | |||||
{ | |||||
await _hubContext.Clients | |||||
.Group(@event.BuyerName) | |||||
.SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents | |||||
{ | |||||
public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToAwaitingValidationIntegrationEvent> | |||||
{ | |||||
private readonly IHubContext<NotificationsHub> _hubContext; | |||||
public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(IHubContext<NotificationsHub> hubContext) | |||||
{ | |||||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) | |||||
{ | |||||
await _hubContext.Clients | |||||
.Group(@event.BuyerName) | |||||
.SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,19 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System.Collections.Generic; | |||||
namespace Ordering.SignalrHub.IntegrationEvents | |||||
{ | |||||
public class OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,23 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToPaidIntegrationEvent(int orderId, | |||||
string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,18 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,26 @@ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Ordering.SignalrHub | |||||
{ | |||||
[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,26 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<Folder Include="wwwroot\" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-preview2-final" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.0.0-preview2-final" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Redis" Version="1.0.0-preview2-final" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@ -0,0 +1,25 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace Ordering.SignalrHub | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static void Main(string[] args) | |||||
{ | |||||
BuildWebHost(args).Run(); | |||||
} | |||||
public static IWebHost BuildWebHost(string[] args) => | |||||
WebHost.CreateDefaultBuilder(args) | |||||
.UseStartup<Startup>() | |||||
.Build(); | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
{ | |||||
"iisSettings": { | |||||
"windowsAuthentication": false, | |||||
"anonymousAuthentication": true, | |||||
"iisExpress": { | |||||
"applicationUrl": "http://localhost:51311/", | |||||
"sslPort": 0 | |||||
} | |||||
}, | |||||
"profiles": { | |||||
"IIS Express": { | |||||
"commandName": "IISExpress", | |||||
"launchBrowser": true, | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
}, | |||||
"Ordering.SignalrHub": { | |||||
"commandName": "Project", | |||||
"launchBrowser": true, | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
}, | |||||
"applicationUrl": "http://localhost:51312/" | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,221 @@ | |||||
using Autofac; | |||||
using Autofac.Extensions.DependencyInjection; | |||||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.Azure.ServiceBus; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using Ordering.SignalrHub.AutofacModules; | |||||
using Ordering.SignalrHub.IntegrationEvents; | |||||
using Ordering.SignalrHub.IntegrationEvents.EventHandling; | |||||
using Ordering.SignalrHub.IntegrationEvents.Events; | |||||
using RabbitMQ.Client; | |||||
using StackExchange.Redis; | |||||
using System; | |||||
using System.IdentityModel.Tokens.Jwt; | |||||
namespace Ordering.SignalrHub | |||||
{ | |||||
public class Startup | |||||
{ | |||||
public Startup(IConfiguration configuration) | |||||
{ | |||||
Configuration = configuration; | |||||
} | |||||
// This method gets called by the runtime. Use this method to add services to the container. | |||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 | |||||
public IServiceProvider ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddCors(options => | |||||
{ | |||||
options.AddPolicy("CorsPolicy", | |||||
builder => builder.AllowAnyOrigin() | |||||
.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials()); | |||||
}); | |||||
if (Configuration.GetValue<string>("IsClusterEnv") == bool.TrueString) | |||||
{ | |||||
services | |||||
.AddSignalR() | |||||
.AddRedis(Configuration["SignalrStoreConnectionString"]); | |||||
//services | |||||
// .AddSignalR() | |||||
// .AddRedis(options => options.Factory = writer => | |||||
// { | |||||
// return ConnectionMultiplexer.Connect(Configuration["SignalrStoreConnectionString"], writer); | |||||
// }); | |||||
} | |||||
else | |||||
{ | |||||
services.AddSignalR(); | |||||
} | |||||
if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
services.AddSingleton<IServiceBusPersisterConnection>(sp => | |||||
{ | |||||
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>(); | |||||
var serviceBusConnectionString = Configuration["EventBusConnection"]; | |||||
var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); | |||||
return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); | |||||
}); | |||||
} | |||||
else | |||||
{ | |||||
services.AddSingleton<IRabbitMQPersistentConnection>(sp => | |||||
{ | |||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); | |||||
var factory = new ConnectionFactory() | |||||
{ | |||||
HostName = Configuration["EventBusConnection"] | |||||
}; | |||||
if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) | |||||
{ | |||||
factory.UserName = Configuration["EventBusUserName"]; | |||||
} | |||||
if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) | |||||
{ | |||||
factory.Password = Configuration["EventBusPassword"]; | |||||
} | |||||
var retryCount = 5; | |||||
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) | |||||
{ | |||||
retryCount = int.Parse(Configuration["EventBusRetryCount"]); | |||||
} | |||||
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); | |||||
}); | |||||
} | |||||
ConfigureAuthService(services); | |||||
RegisterEventBus(services); | |||||
services.AddOptions(); | |||||
//configure autofac | |||||
var container = new ContainerBuilder(); | |||||
container.RegisterModule(new ApplicationModule()); | |||||
container.Populate(services); | |||||
return new AutofacServiceProvider(container.Build()); | |||||
} | |||||
public IConfiguration Configuration { get; } | |||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) | |||||
{ | |||||
loggerFactory.AddConsole(Configuration.GetSection("Logging")); | |||||
loggerFactory.AddDebug(); | |||||
loggerFactory.AddAzureWebAppDiagnostics(); | |||||
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); | |||||
var pathBase = Configuration["PATH_BASE"]; | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
app.UseCors("CorsPolicy"); | |||||
app.UseAuthentication(); | |||||
app.UseSignalR(routes => | |||||
{ | |||||
routes.MapHub<NotificationsHub>("/notificationhub", options => | |||||
options.Transports = Microsoft.AspNetCore.Http.Connections.TransportType.All); | |||||
}); | |||||
ConfigureEventBus(app); | |||||
} | |||||
private void ConfigureEventBus(IApplicationBuilder app) | |||||
{ | |||||
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); | |||||
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>(); | |||||
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>(); | |||||
eventBus.Subscribe<OrderStatusChangedToStockConfirmedIntegrationEvent, OrderStatusChangedToStockConfirmedIntegrationEventHandler>(); | |||||
eventBus.Subscribe<OrderStatusChangedToShippedIntegrationEvent, OrderStatusChangedToShippedIntegrationEventHandler>(); | |||||
eventBus.Subscribe<OrderStatusChangedToCancelledIntegrationEvent, OrderStatusChangedToCancelledIntegrationEventHandler>(); | |||||
eventBus.Subscribe<OrderStatusChangedToSubmittedIntegrationEvent, OrderStatusChangedToSubmittedIntegrationEventHandler>(); | |||||
} | |||||
private void ConfigureAuthService(IServiceCollection services) | |||||
{ | |||||
// prevent from mapping "sub" claim to nameidentifier. | |||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | |||||
var identityUrl = Configuration.GetValue<string>("IdentityUrl"); | |||||
services.AddAuthentication(options => | |||||
{ | |||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
}).AddJwtBearer(options => | |||||
{ | |||||
options.Authority = identityUrl; | |||||
options.RequireHttpsMetadata = false; | |||||
options.Audience = "orders.signalrhub"; | |||||
}); | |||||
} | |||||
private void RegisterEventBus(IServiceCollection services) | |||||
{ | |||||
var subscriptionClientName = Configuration["SubscriptionClientName"]; | |||||
if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | |||||
{ | |||||
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | |||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | |||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | |||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger, | |||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | |||||
}); | |||||
} | |||||
else | |||||
{ | |||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | |||||
{ | |||||
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | |||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | |||||
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | |||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||||
var retryCount = 5; | |||||
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) | |||||
{ | |||||
retryCount = int.Parse(Configuration["EventBusRetryCount"]); | |||||
} | |||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | |||||
}); | |||||
} | |||||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,15 @@ | |||||
{ | |||||
"IdentityUrl": "http://localhost:5105", | |||||
"Logging": { | |||||
"IncludeScopes": false, | |||||
"LogLevel": { | |||||
"Default": "Trace", | |||||
"System": "Information", | |||||
"Microsoft": "Information" | |||||
} | |||||
}, | |||||
"AzureServiceBusEnabled": false, | |||||
"SubscriptionClientName": "Ordering.signalrhub", | |||||
"EventBusRetryCount": 5, | |||||
"EventBusConnection": "localhost" | |||||
} |
@ -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" | |||||
} | |||||
} |
@ -0,0 +1,228 @@ | |||||
.toast-title { | |||||
font-weight: bold; | |||||
} | |||||
.toast-message { | |||||
-ms-word-wrap: break-word; | |||||
word-wrap: break-word; | |||||
} | |||||
.toast-message a, | |||||
.toast-message label { | |||||
color: #FFFFFF; | |||||
} | |||||
.toast-message a:hover { | |||||
color: #CCCCCC; | |||||
text-decoration: none; | |||||
} | |||||
.toast-close-button { | |||||
position: relative; | |||||
right: -0.3em; | |||||
top: -0.3em; | |||||
float: right; | |||||
font-size: 20px; | |||||
font-weight: bold; | |||||
color: #FFFFFF; | |||||
-webkit-text-shadow: 0 1px 0 #ffffff; | |||||
text-shadow: 0 1px 0 #ffffff; | |||||
opacity: 0.8; | |||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); | |||||
filter: alpha(opacity=80); | |||||
line-height: 1; | |||||
} | |||||
.toast-close-button:hover, | |||||
.toast-close-button:focus { | |||||
color: #000000; | |||||
text-decoration: none; | |||||
cursor: pointer; | |||||
opacity: 0.4; | |||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40); | |||||
filter: alpha(opacity=40); | |||||
} | |||||
.rtl .toast-close-button { | |||||
left: -0.3em; | |||||
float: left; | |||||
right: 0.3em; | |||||
} | |||||
/*Additional properties for button version | |||||
iOS requires the button element instead of an anchor tag. | |||||
If you want the anchor version, it requires `href="#"`.*/ | |||||
button.toast-close-button { | |||||
padding: 0; | |||||
cursor: pointer; | |||||
background: transparent; | |||||
border: 0; | |||||
-webkit-appearance: none; | |||||
} | |||||
.toast-top-center { | |||||
top: 0; | |||||
right: 0; | |||||
width: 100%; | |||||
} | |||||
.toast-bottom-center { | |||||
bottom: 0; | |||||
right: 0; | |||||
width: 100%; | |||||
} | |||||
.toast-top-full-width { | |||||
top: 0; | |||||
right: 0; | |||||
width: 100%; | |||||
} | |||||
.toast-bottom-full-width { | |||||
bottom: 0; | |||||
right: 0; | |||||
width: 100%; | |||||
} | |||||
.toast-top-left { | |||||
top: 12px; | |||||
left: 12px; | |||||
} | |||||
.toast-top-right { | |||||
top: 12px; | |||||
right: 12px; | |||||
} | |||||
.toast-bottom-right { | |||||
right: 12px; | |||||
bottom: 12px; | |||||
} | |||||
.toast-bottom-left { | |||||
bottom: 12px; | |||||
left: 12px; | |||||
} | |||||
#toast-container { | |||||
position: fixed; | |||||
z-index: 999999; | |||||
pointer-events: none; | |||||
/*overrides*/ | |||||
} | |||||
#toast-container * { | |||||
-moz-box-sizing: border-box; | |||||
-webkit-box-sizing: border-box; | |||||
box-sizing: border-box; | |||||
} | |||||
#toast-container > div { | |||||
position: relative; | |||||
pointer-events: auto; | |||||
overflow: hidden; | |||||
margin: 0 0 6px; | |||||
padding: 15px 15px 15px 50px; | |||||
width: 300px; | |||||
-moz-border-radius: 3px 3px 3px 3px; | |||||
-webkit-border-radius: 3px 3px 3px 3px; | |||||
border-radius: 3px 3px 3px 3px; | |||||
background-position: 15px center; | |||||
background-repeat: no-repeat; | |||||
-moz-box-shadow: 0 0 12px #999999; | |||||
-webkit-box-shadow: 0 0 12px #999999; | |||||
box-shadow: 0 0 12px #999999; | |||||
color: #FFFFFF; | |||||
opacity: 0.8; | |||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); | |||||
filter: alpha(opacity=80); | |||||
} | |||||
#toast-container > div.rtl { | |||||
direction: rtl; | |||||
padding: 15px 50px 15px 15px; | |||||
background-position: right 15px center; | |||||
} | |||||
#toast-container > div:hover { | |||||
-moz-box-shadow: 0 0 12px #000000; | |||||
-webkit-box-shadow: 0 0 12px #000000; | |||||
box-shadow: 0 0 12px #000000; | |||||
opacity: 1; | |||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); | |||||
filter: alpha(opacity=100); | |||||
cursor: pointer; | |||||
} | |||||
#toast-container > .toast-info { | |||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important; | |||||
} | |||||
#toast-container > .toast-error { | |||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important; | |||||
} | |||||
#toast-container > .toast-success { | |||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important; | |||||
} | |||||
#toast-container > .toast-warning { | |||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important; | |||||
} | |||||
#toast-container.toast-top-center > div, | |||||
#toast-container.toast-bottom-center > div { | |||||
width: 300px; | |||||
margin-left: auto; | |||||
margin-right: auto; | |||||
} | |||||
#toast-container.toast-top-full-width > div, | |||||
#toast-container.toast-bottom-full-width > div { | |||||
width: 96%; | |||||
margin-left: auto; | |||||
margin-right: auto; | |||||
} | |||||
.toast { | |||||
background-color: #030303; | |||||
} | |||||
.toast-success { | |||||
background-color: #51A351; | |||||
} | |||||
.toast-error { | |||||
background-color: #BD362F; | |||||
} | |||||
.toast-info { | |||||
background-color: #2F96B4; | |||||
} | |||||
.toast-warning { | |||||
background-color: #F89406; | |||||
} | |||||
.toast-progress { | |||||
position: absolute; | |||||
left: 0; | |||||
bottom: 0; | |||||
height: 4px; | |||||
background-color: #000000; | |||||
opacity: 0.4; | |||||
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40); | |||||
filter: alpha(opacity=40); | |||||
} | |||||
/*Responsive Design*/ | |||||
@media all and (max-width: 240px) { | |||||
#toast-container > div { | |||||
padding: 8px 8px 8px 50px; | |||||
width: 11em; | |||||
} | |||||
#toast-container > div.rtl { | |||||
padding: 8px 50px 8px 8px; | |||||
} | |||||
#toast-container .toast-close-button { | |||||
right: -0.2em; | |||||
top: -0.2em; | |||||
} | |||||
#toast-container .rtl .toast-close-button { | |||||
left: -0.2em; | |||||
right: 0.2em; | |||||
} | |||||
} | |||||
@media all and (min-width: 241px) and (max-width: 480px) { | |||||
#toast-container > div { | |||||
padding: 8px 8px 8px 50px; | |||||
width: 18em; | |||||
} | |||||
#toast-container > div.rtl { | |||||
padding: 8px 50px 8px 8px; | |||||
} | |||||
#toast-container .toast-close-button { | |||||
right: -0.2em; | |||||
top: -0.2em; | |||||
} | |||||
#toast-container .rtl .toast-close-button { | |||||
left: -0.2em; | |||||
right: 0.2em; | |||||
} | |||||
} | |||||
@media all and (min-width: 481px) and (max-width: 768px) { | |||||
#toast-container > div { | |||||
padding: 15px 15px 15px 50px; | |||||
width: 25em; | |||||
} | |||||
#toast-container > div.rtl { | |||||
padding: 15px 50px 15px 15px; | |||||
} | |||||
} |
@ -0,0 +1,476 @@ | |||||
/* | |||||
* Toastr | |||||
* Copyright 2012-2015 | |||||
* Authors: John Papa, Hans Fjällemark, and Tim Ferrell. | |||||
* All Rights Reserved. | |||||
* Use, reproduction, distribution, and modification of this code is subject to the terms and | |||||
* conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php | |||||
* | |||||
* ARIA Support: Greta Krafsig | |||||
* | |||||
* Project: https://github.com/CodeSeven/toastr | |||||
*/ | |||||
/* global define */ | |||||
(function (define) { | |||||
define(['jquery'], function ($) { | |||||
return (function () { | |||||
var $container; | |||||
var listener; | |||||
var toastId = 0; | |||||
var toastType = { | |||||
error: 'error', | |||||
info: 'info', | |||||
success: 'success', | |||||
warning: 'warning' | |||||
}; | |||||
var toastr = { | |||||
clear: clear, | |||||
remove: remove, | |||||
error: error, | |||||
getContainer: getContainer, | |||||
info: info, | |||||
options: {}, | |||||
subscribe: subscribe, | |||||
success: success, | |||||
version: '2.1.4', | |||||
warning: warning | |||||
}; | |||||
var previousToast; | |||||
return toastr; | |||||
//////////////// | |||||
function error(message, title, optionsOverride) { | |||||
return notify({ | |||||
type: toastType.error, | |||||
iconClass: getOptions().iconClasses.error, | |||||
message: message, | |||||
optionsOverride: optionsOverride, | |||||
title: title | |||||
}); | |||||
} | |||||
function getContainer(options, create) { | |||||
if (!options) { options = getOptions(); } | |||||
$container = $('#' + options.containerId); | |||||
if ($container.length) { | |||||
return $container; | |||||
} | |||||
if (create) { | |||||
$container = createContainer(options); | |||||
} | |||||
return $container; | |||||
} | |||||
function info(message, title, optionsOverride) { | |||||
return notify({ | |||||
type: toastType.info, | |||||
iconClass: getOptions().iconClasses.info, | |||||
message: message, | |||||
optionsOverride: optionsOverride, | |||||
title: title | |||||
}); | |||||
} | |||||
function subscribe(callback) { | |||||
listener = callback; | |||||
} | |||||
function success(message, title, optionsOverride) { | |||||
return notify({ | |||||
type: toastType.success, | |||||
iconClass: getOptions().iconClasses.success, | |||||
message: message, | |||||
optionsOverride: optionsOverride, | |||||
title: title | |||||
}); | |||||
} | |||||
function warning(message, title, optionsOverride) { | |||||
return notify({ | |||||
type: toastType.warning, | |||||
iconClass: getOptions().iconClasses.warning, | |||||
message: message, | |||||
optionsOverride: optionsOverride, | |||||
title: title | |||||
}); | |||||
} | |||||
function clear($toastElement, clearOptions) { | |||||
var options = getOptions(); | |||||
if (!$container) { getContainer(options); } | |||||
if (!clearToast($toastElement, options, clearOptions)) { | |||||
clearContainer(options); | |||||
} | |||||
} | |||||
function remove($toastElement) { | |||||
var options = getOptions(); | |||||
if (!$container) { getContainer(options); } | |||||
if ($toastElement && $(':focus', $toastElement).length === 0) { | |||||
removeToast($toastElement); | |||||
return; | |||||
} | |||||
if ($container.children().length) { | |||||
$container.remove(); | |||||
} | |||||
} | |||||
// internal functions | |||||
function clearContainer (options) { | |||||
var toastsToClear = $container.children(); | |||||
for (var i = toastsToClear.length - 1; i >= 0; i--) { | |||||
clearToast($(toastsToClear[i]), options); | |||||
} | |||||
} | |||||
function clearToast ($toastElement, options, clearOptions) { | |||||
var force = clearOptions && clearOptions.force ? clearOptions.force : false; | |||||
if ($toastElement && (force || $(':focus', $toastElement).length === 0)) { | |||||
$toastElement[options.hideMethod]({ | |||||
duration: options.hideDuration, | |||||
easing: options.hideEasing, | |||||
complete: function () { removeToast($toastElement); } | |||||
}); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
function createContainer(options) { | |||||
$container = $('<div/>') | |||||
.attr('id', options.containerId) | |||||
.addClass(options.positionClass); | |||||
$container.appendTo($(options.target)); | |||||
return $container; | |||||
} | |||||
function getDefaults() { | |||||
return { | |||||
tapToDismiss: true, | |||||
toastClass: 'toast', | |||||
containerId: 'toast-container', | |||||
debug: false, | |||||
showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery | |||||
showDuration: 300, | |||||
showEasing: 'swing', //swing and linear are built into jQuery | |||||
onShown: undefined, | |||||
hideMethod: 'fadeOut', | |||||
hideDuration: 1000, | |||||
hideEasing: 'swing', | |||||
onHidden: undefined, | |||||
closeMethod: false, | |||||
closeDuration: false, | |||||
closeEasing: false, | |||||
closeOnHover: true, | |||||
extendedTimeOut: 1000, | |||||
iconClasses: { | |||||
error: 'toast-error', | |||||
info: 'toast-info', | |||||
success: 'toast-success', | |||||
warning: 'toast-warning' | |||||
}, | |||||
iconClass: 'toast-info', | |||||
positionClass: 'toast-top-right', | |||||
timeOut: 5000, // Set timeOut and extendedTimeOut to 0 to make it sticky | |||||
titleClass: 'toast-title', | |||||
messageClass: 'toast-message', | |||||
escapeHtml: false, | |||||
target: 'body', | |||||
closeHtml: '<button type="button">×</button>', | |||||
closeClass: 'toast-close-button', | |||||
newestOnTop: true, | |||||
preventDuplicates: false, | |||||
progressBar: false, | |||||
progressClass: 'toast-progress', | |||||
rtl: false | |||||
}; | |||||
} | |||||
function publish(args) { | |||||
if (!listener) { return; } | |||||
listener(args); | |||||
} | |||||
function notify(map) { | |||||
var options = getOptions(); | |||||
var iconClass = map.iconClass || options.iconClass; | |||||
if (typeof (map.optionsOverride) !== 'undefined') { | |||||
options = $.extend(options, map.optionsOverride); | |||||
iconClass = map.optionsOverride.iconClass || iconClass; | |||||
} | |||||
if (shouldExit(options, map)) { return; } | |||||
toastId++; | |||||
$container = getContainer(options, true); | |||||
var intervalId = null; | |||||
var $toastElement = $('<div/>'); | |||||
var $titleElement = $('<div/>'); | |||||
var $messageElement = $('<div/>'); | |||||
var $progressElement = $('<div/>'); | |||||
var $closeElement = $(options.closeHtml); | |||||
var progressBar = { | |||||
intervalId: null, | |||||
hideEta: null, | |||||
maxHideTime: null | |||||
}; | |||||
var response = { | |||||
toastId: toastId, | |||||
state: 'visible', | |||||
startTime: new Date(), | |||||
options: options, | |||||
map: map | |||||
}; | |||||
personalizeToast(); | |||||
displayToast(); | |||||
handleEvents(); | |||||
publish(response); | |||||
if (options.debug && console) { | |||||
console.log(response); | |||||
} | |||||
return $toastElement; | |||||
function escapeHtml(source) { | |||||
if (source == null) { | |||||
source = ''; | |||||
} | |||||
return source | |||||
.replace(/&/g, '&') | |||||
.replace(/"/g, '"') | |||||
.replace(/'/g, ''') | |||||
.replace(/</g, '<') | |||||
.replace(/>/g, '>'); | |||||
} | |||||
function personalizeToast() { | |||||
setIcon(); | |||||
setTitle(); | |||||
setMessage(); | |||||
setCloseButton(); | |||||
setProgressBar(); | |||||
setRTL(); | |||||
setSequence(); | |||||
setAria(); | |||||
} | |||||
function setAria() { | |||||
var ariaValue = ''; | |||||
switch (map.iconClass) { | |||||
case 'toast-success': | |||||
case 'toast-info': | |||||
ariaValue = 'polite'; | |||||
break; | |||||
default: | |||||
ariaValue = 'assertive'; | |||||
} | |||||
$toastElement.attr('aria-live', ariaValue); | |||||
} | |||||
function handleEvents() { | |||||
if (options.closeOnHover) { | |||||
$toastElement.hover(stickAround, delayedHideToast); | |||||
} | |||||
if (!options.onclick && options.tapToDismiss) { | |||||
$toastElement.click(hideToast); | |||||
} | |||||
if (options.closeButton && $closeElement) { | |||||
$closeElement.click(function (event) { | |||||
if (event.stopPropagation) { | |||||
event.stopPropagation(); | |||||
} else if (event.cancelBubble !== undefined && event.cancelBubble !== true) { | |||||
event.cancelBubble = true; | |||||
} | |||||
if (options.onCloseClick) { | |||||
options.onCloseClick(event); | |||||
} | |||||
hideToast(true); | |||||
}); | |||||
} | |||||
if (options.onclick) { | |||||
$toastElement.click(function (event) { | |||||
options.onclick(event); | |||||
hideToast(); | |||||
}); | |||||
} | |||||
} | |||||
function displayToast() { | |||||
$toastElement.hide(); | |||||
$toastElement[options.showMethod]( | |||||
{duration: options.showDuration, easing: options.showEasing, complete: options.onShown} | |||||
); | |||||
if (options.timeOut > 0) { | |||||
intervalId = setTimeout(hideToast, options.timeOut); | |||||
progressBar.maxHideTime = parseFloat(options.timeOut); | |||||
progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime; | |||||
if (options.progressBar) { | |||||
progressBar.intervalId = setInterval(updateProgress, 10); | |||||
} | |||||
} | |||||
} | |||||
function setIcon() { | |||||
if (map.iconClass) { | |||||
$toastElement.addClass(options.toastClass).addClass(iconClass); | |||||
} | |||||
} | |||||
function setSequence() { | |||||
if (options.newestOnTop) { | |||||
$container.prepend($toastElement); | |||||
} else { | |||||
$container.append($toastElement); | |||||
} | |||||
} | |||||
function setTitle() { | |||||
if (map.title) { | |||||
var suffix = map.title; | |||||
if (options.escapeHtml) { | |||||
suffix = escapeHtml(map.title); | |||||
} | |||||
$titleElement.append(suffix).addClass(options.titleClass); | |||||
$toastElement.append($titleElement); | |||||
} | |||||
} | |||||
function setMessage() { | |||||
if (map.message) { | |||||
var suffix = map.message; | |||||
if (options.escapeHtml) { | |||||
suffix = escapeHtml(map.message); | |||||
} | |||||
$messageElement.append(suffix).addClass(options.messageClass); | |||||
$toastElement.append($messageElement); | |||||
} | |||||
} | |||||
function setCloseButton() { | |||||
if (options.closeButton) { | |||||
$closeElement.addClass(options.closeClass).attr('role', 'button'); | |||||
$toastElement.prepend($closeElement); | |||||
} | |||||
} | |||||
function setProgressBar() { | |||||
if (options.progressBar) { | |||||
$progressElement.addClass(options.progressClass); | |||||
$toastElement.prepend($progressElement); | |||||
} | |||||
} | |||||
function setRTL() { | |||||
if (options.rtl) { | |||||
$toastElement.addClass('rtl'); | |||||
} | |||||
} | |||||
function shouldExit(options, map) { | |||||
if (options.preventDuplicates) { | |||||
if (map.message === previousToast) { | |||||
return true; | |||||
} else { | |||||
previousToast = map.message; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
function hideToast(override) { | |||||
var method = override && options.closeMethod !== false ? options.closeMethod : options.hideMethod; | |||||
var duration = override && options.closeDuration !== false ? | |||||
options.closeDuration : options.hideDuration; | |||||
var easing = override && options.closeEasing !== false ? options.closeEasing : options.hideEasing; | |||||
if ($(':focus', $toastElement).length && !override) { | |||||
return; | |||||
} | |||||
clearTimeout(progressBar.intervalId); | |||||
return $toastElement[method]({ | |||||
duration: duration, | |||||
easing: easing, | |||||
complete: function () { | |||||
removeToast($toastElement); | |||||
clearTimeout(intervalId); | |||||
if (options.onHidden && response.state !== 'hidden') { | |||||
options.onHidden(); | |||||
} | |||||
response.state = 'hidden'; | |||||
response.endTime = new Date(); | |||||
publish(response); | |||||
} | |||||
}); | |||||
} | |||||
function delayedHideToast() { | |||||
if (options.timeOut > 0 || options.extendedTimeOut > 0) { | |||||
intervalId = setTimeout(hideToast, options.extendedTimeOut); | |||||
progressBar.maxHideTime = parseFloat(options.extendedTimeOut); | |||||
progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime; | |||||
} | |||||
} | |||||
function stickAround() { | |||||
clearTimeout(intervalId); | |||||
progressBar.hideEta = 0; | |||||
$toastElement.stop(true, true)[options.showMethod]( | |||||
{duration: options.showDuration, easing: options.showEasing} | |||||
); | |||||
} | |||||
function updateProgress() { | |||||
var percentage = ((progressBar.hideEta - (new Date().getTime())) / progressBar.maxHideTime) * 100; | |||||
$progressElement.width(percentage + '%'); | |||||
} | |||||
} | |||||
function getOptions() { | |||||
return $.extend({}, getDefaults(), toastr.options); | |||||
} | |||||
function removeToast($toastElement) { | |||||
if (!$container) { $container = getContainer(); } | |||||
if ($toastElement.is(':visible')) { | |||||
return; | |||||
} | |||||
$toastElement.remove(); | |||||
$toastElement = null; | |||||
if ($container.children().length === 0) { | |||||
$container.remove(); | |||||
previousToast = undefined; | |||||
} | |||||
} | |||||
})(); | |||||
}); | |||||
}(typeof define === 'function' && define.amd ? define : function (deps, factory) { | |||||
if (typeof module !== 'undefined' && module.exports) { //Node | |||||
module.exports = factory(require('jquery')); | |||||
} else { | |||||
window.toastr = factory(window.jQuery); | |||||
} | |||||
})); |