diff --git a/docker-compose.override.yml b/docker-compose.override.yml
index 19c255c6e..3457d8b9f 100644
--- a/docker-compose.override.yml
+++ b/docker-compose.override.yml
@@ -7,7 +7,12 @@ version: '2'
# An external IP or DNS name has to be used (instead localhost and the 10.0.75.1 IP) when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance.
services:
-
+ sagamanager:
+ environment:
+ - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
+ - EventBusConnection=rabbitmq
+ - GracePeriod=15 #In minutes
+
basket.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
diff --git a/docker-compose.vs.debug.yml b/docker-compose.vs.debug.yml
index 88ebfc47c..eaa0b6f35 100644
--- a/docker-compose.vs.debug.yml
+++ b/docker-compose.vs.debug.yml
@@ -120,3 +120,16 @@ services:
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
+
+ sagamanager:
+ image: eshop/sagamanager:dev
+ build:
+ args:
+ source: ${DOCKER_BUILD_SOURCE}
+ volumes:
+ - ./src/Services/SagaManager/SagaManager:/app
+ - ~/.nuget/packages:/root/.nuget/packages:ro
+ - ~/clrdbg:/clrdbg:ro
+ entrypoint: tail -f /dev/null
+ labels:
+ - "com.microsoft.visualstudio.targetoperatingsystem=linux"
diff --git a/docker-compose.vs.release.yml b/docker-compose.vs.release.yml
index 9a51a08f5..a0c9d890d 100644
--- a/docker-compose.vs.release.yml
+++ b/docker-compose.vs.release.yml
@@ -80,3 +80,13 @@ services:
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
+
+ sagamanager:
+ build:
+ args:
+ source: ${DOCKER_BUILD_SOURCE}
+ volumes:
+ - ~/clrdbg:/clrdbg:ro
+ entrypoint: tail -f /dev/null
+ labels:
+ - "com.microsoft.visualstudio.targetoperatingsystem=linux"
diff --git a/docker-compose.yml b/docker-compose.yml
index 718bfc5f5..cc134a005 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,16 @@
version: '2'
+
services:
+ sagamanager:
+ image: eshop/sagamanager
+ build:
+ context: ./src/Services/SagaManager/SagaManager
+ dockerfile: Dockerfile
+ depends_on:
+ - sql.data
+ - rabbitmq
+
basket.api:
image: eshop/basket.api
build:
@@ -9,7 +19,6 @@ services:
depends_on:
- basket.data
- identity.api
- - rabbitmq
catalog.api:
image: eshop/catalog.api
@@ -35,6 +44,7 @@ services:
dockerfile: Dockerfile
depends_on:
- sql.data
+ - rabbitmq
webspa:
image: eshop/webspa
diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln
index f5c4c7384..d59b891f6 100644
--- a/eShopOnContainers-ServicesAndWebApps.sln
+++ b/eShopOnContainers-ServicesAndWebApps.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26403.3
+VisualStudioVersion = 15.0.26403.7
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
EndProject
@@ -78,6 +78,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SagaManager", "SagaManager", "{F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SagaManager", "src\Services\SagaManager\SagaManager\SagaManager.csproj", "{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -1004,6 +1008,54 @@ Global
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.ActiveCfg = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.Build.0 = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|ARM.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|x64.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|x64.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|x86.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|ARM.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|x64.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|x86.Build.0 = Debug|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|ARM.ActiveCfg = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|ARM.Build.0 = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|iPhone.Build.0 = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|x64.ActiveCfg = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|x64.Build.0 = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|x86.ActiveCfg = Release|Any CPU
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1041,5 +1093,7 @@ Global
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502} = {FBF43D93-F2E7-4FF8-B4AB-186895949B88}
{022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5}
+ {F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
+ {F6E0F0DD-1400-43C3-B5E0-7CC325728C47} = {F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E}
EndGlobalSection
EndGlobal
diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs
index a0767459e..f9d8b8923 100644
--- a/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs
+++ b/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs
@@ -1,15 +1,12 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
namespace Ordering.API.Application.IntegrationCommands.Commands
{
public class ConfirmGracePeriodCommandMsg : IntegrationEvent
{
- public int OrderNumber { get; private set; }
+ public int OrderId { get; }
- //TODO: message should change to Integration command type once command bus is implemented
+ public ConfirmGracePeriodCommandMsg(int orderId) =>
+ OrderId = orderId;
}
}
diff --git a/src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs b/src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs
index 32df5b327..c31c35e6d 100644
--- a/src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs
+++ b/src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs
@@ -10,6 +10,7 @@ using Ordering.API.Application.IntegrationCommands.Commands;
using Ordering.Domain.Exceptions;
using System;
using System.Threading.Tasks;
+using Ordering.API.Application.IntegrationEvents.Events;
namespace Ordering.API.Application.Sagas
{
@@ -73,13 +74,17 @@ namespace Ordering.API.Application.Sagas
///
public async Task Handle(ConfirmGracePeriodCommandMsg command)
{
- var orderSaga = FindSagaById(command.OrderNumber);
+ var orderSaga = FindSagaById(command.OrderId);
CheckValidSagaId(orderSaga);
// TODO: This handler should change to Integration command handler type once command bus is implemented
-
// TODO: If order status is not cancelled, change state to awaitingValidation and
- // send ConfirmOrderStockCommandMsg to Inventory api
+ // send ConfirmOrderStockCommandMsg to Inventory api
+ if (orderSaga.OrderStatus == OrderStatus.Submitted)
+ {
+
+ }
+
}
diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj
index ba35d75e2..445f93ad8 100644
--- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj
+++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj
@@ -13,6 +13,13 @@
..\..\..\..\docker-compose.dcproj
+
+
+
+
+
+
+
PreserveNewest
@@ -80,7 +87,6 @@
-
diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs
index 4ecb5231c..82bbf6fd4 100644
--- a/src/Services/Ordering/Ordering.API/Startup.cs
+++ b/src/Services/Ordering/Ordering.API/Startup.cs
@@ -1,4 +1,6 @@
-namespace Microsoft.eShopOnContainers.Services.Ordering.API
+
+
+namespace Microsoft.eShopOnContainers.Services.Ordering.API
{
using AspNetCore.Http;
using Autofac;
@@ -21,6 +23,9 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.HealthChecks;
using Microsoft.Extensions.Logging;
+ using Ordering.API.Application.Commands;
+ using Ordering.API.Application.IntegrationCommands.Commands;
+ using Ordering.API.Application.IntegrationEvents.Events;
using Ordering.Infrastructure;
using RabbitMQ.Client;
using System;
@@ -157,6 +162,8 @@
.UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Ordering.API"))
.Options);
integrationEventLogContext.Database.Migrate();
+
+ ConfigureEventBus(app);
}
protected virtual void ConfigureAuth(IApplicationBuilder app)
@@ -169,5 +176,16 @@
RequireHttpsMetadata = false
});
}
+
+ protected virtual void ConfigureEventBus(IApplicationBuilder app)
+ {
+ var confirmGracePeriodHandler = app.ApplicationServices
+ .GetService>();
+
+ var eventBus = app.ApplicationServices
+ .GetRequiredService();
+
+ eventBus.Subscribe(confirmGracePeriodHandler);
+ }
}
}
diff --git a/src/Services/SagaManager/SagaManager/.dockerignore b/src/Services/SagaManager/SagaManager/.dockerignore
new file mode 100644
index 000000000..d8f8175f6
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/.dockerignore
@@ -0,0 +1,3 @@
+*
+!obj/Docker/publish/*
+!obj/Docker/empty/
diff --git a/src/Services/SagaManager/SagaManager/Dockerfile b/src/Services/SagaManager/SagaManager/Dockerfile
new file mode 100644
index 000000000..f8adb603a
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/Dockerfile
@@ -0,0 +1,5 @@
+FROM microsoft/dotnet:1.1-runtime
+ARG source
+WORKDIR /app
+COPY ${source:-obj/Docker/publish} .
+ENTRYPOINT ["dotnet", "SagaManager.dll"]
\ No newline at end of file
diff --git a/src/Services/SagaManager/SagaManager/IntegrationEvents/ConfirmGracePeriodEvent.cs b/src/Services/SagaManager/SagaManager/IntegrationEvents/ConfirmGracePeriodEvent.cs
new file mode 100644
index 000000000..36f88c9be
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/IntegrationEvents/ConfirmGracePeriodEvent.cs
@@ -0,0 +1,16 @@
+namespace SagaManager.IntegrationEvents
+{
+ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+ using System;
+
+ public class ConfirmGracePeriodEvent : IConfirmGracePeriodEvent
+ {
+ private readonly IEventBus _eventBus;
+
+ public ConfirmGracePeriodEvent(IEventBus eventBus) =>
+ _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
+
+ public void PublishThroughEventBus(IntegrationEvent evt) => _eventBus.Publish(evt);
+ }
+}
\ No newline at end of file
diff --git a/src/Services/SagaManager/SagaManager/IntegrationEvents/Events/ConfirmGracePeriodIntegrationEvent.cs b/src/Services/SagaManager/SagaManager/IntegrationEvents/Events/ConfirmGracePeriodIntegrationEvent.cs
new file mode 100644
index 000000000..680328301
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/IntegrationEvents/Events/ConfirmGracePeriodIntegrationEvent.cs
@@ -0,0 +1,14 @@
+namespace SagaManager.IntegrationEvents.Events
+{
+ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+
+ // Integration Events notes:
+ // An Event is “something that has happened in the past”, therefore its name has to be
+ // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems.
+ public class ConfirmGracePeriodIntegrationEvent : IntegrationEvent
+ {
+ public int OrderId { get;}
+
+ public ConfirmGracePeriodIntegrationEvent(int orderId) => OrderId = orderId;
+ }
+}
diff --git a/src/Services/SagaManager/SagaManager/IntegrationEvents/IConfirmGracePeriodEvent.cs b/src/Services/SagaManager/SagaManager/IntegrationEvents/IConfirmGracePeriodEvent.cs
new file mode 100644
index 000000000..236860db2
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/IntegrationEvents/IConfirmGracePeriodEvent.cs
@@ -0,0 +1,9 @@
+namespace SagaManager.IntegrationEvents
+{
+ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+
+ public interface IConfirmGracePeriodEvent
+ {
+ void PublishThroughEventBus(IntegrationEvent evt);
+ }
+}
\ No newline at end of file
diff --git a/src/Services/SagaManager/SagaManager/Program.cs b/src/Services/SagaManager/SagaManager/Program.cs
new file mode 100644
index 000000000..d76a8a080
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/Program.cs
@@ -0,0 +1,65 @@
+using SagaManager.IntegrationEvents;
+
+namespace SagaManager
+{
+ using System.IO;
+ using System;
+ using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+ using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
+ using Microsoft.Extensions.Logging;
+ using Microsoft.Extensions.Options;
+ using RabbitMQ.Client;
+ using Services;
+
+ public class Program
+ {
+ public static IConfigurationRoot Configuration { get; set; }
+
+ public static void Main(string[] args)
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
+ .AddEnvironmentVariables();
+
+ Configuration = builder.Build();
+
+ var serviceProvider = new ServiceCollection()
+ .AddLogging()
+ .AddOptions()
+ .Configure(Configuration)
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton(sp =>
+ {
+ var settings = sp.GetRequiredService>().Value;
+ var logger = sp.GetRequiredService>();
+ var factory = new ConnectionFactory()
+ {
+ HostName = settings.EventBusConnection
+ };
+
+ return new DefaultRabbitMQPersisterConnection(factory, logger);
+ })
+ .AddSingleton()
+ .BuildServiceProvider();
+
+ //configure console logging
+ serviceProvider
+ .GetService()
+ .AddConsole(Configuration.GetSection("Logging"))
+ .AddConsole(LogLevel.Debug);
+
+ var sagaManagerService = serviceProvider
+ .GetRequiredService();
+
+ while (true)
+ {
+ sagaManagerService.CheckFinishedGracePeriodOrders();
+ System.Threading.Thread.Sleep(30000);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Services/SagaManager/SagaManager/SagaManager.csproj b/src/Services/SagaManager/SagaManager/SagaManager.csproj
new file mode 100644
index 000000000..47b8b58c2
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/SagaManager.csproj
@@ -0,0 +1,32 @@
+
+
+
+ Exe
+ netcoreapp1.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Dockerfile
+
+
+
+
\ No newline at end of file
diff --git a/src/Services/SagaManager/SagaManager/SagaManagerSettings.cs b/src/Services/SagaManager/SagaManager/SagaManagerSettings.cs
new file mode 100644
index 000000000..ab9184b8a
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/SagaManagerSettings.cs
@@ -0,0 +1,11 @@
+namespace SagaManager
+{
+ public class SagaManagerSettings
+ {
+ public string ConnectionString { get; set; }
+
+ public string EventBusConnection { get; set; }
+
+ public int GracePeriod { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Services/SagaManager/SagaManager/Services/ISagaManagerService.cs b/src/Services/SagaManager/SagaManager/Services/ISagaManagerService.cs
new file mode 100644
index 000000000..dc027b29c
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/Services/ISagaManagerService.cs
@@ -0,0 +1,7 @@
+namespace SagaManager.Services
+{
+ public interface ISagaManagerService
+ {
+ void CheckFinishedGracePeriodOrders();
+ }
+}
\ No newline at end of file
diff --git a/src/Services/SagaManager/SagaManager/Services/SagaManagerService.cs b/src/Services/SagaManager/SagaManager/Services/SagaManagerService.cs
new file mode 100644
index 000000000..991e62029
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/Services/SagaManagerService.cs
@@ -0,0 +1,59 @@
+namespace SagaManager.Services
+{
+ using System.Collections.Generic;
+ using System.Data.SqlClient;
+ using Microsoft.Extensions.Options;
+ using Dapper;
+ using IntegrationEvents;
+ using IntegrationEvents.Events;
+
+ public class SagaManagerService : ISagaManagerService
+ {
+ private readonly SagaManagerSettings _settings;
+ private readonly IConfirmGracePeriodEvent _confirmGracePeriodEvent;
+
+ public SagaManagerService(IOptions settings,
+ IConfirmGracePeriodEvent confirmGracePeriodEvent)
+ {
+ _settings = settings.Value;
+ _confirmGracePeriodEvent = confirmGracePeriodEvent;
+ }
+
+ public void CheckFinishedGracePeriodOrders()
+ {
+ var orderIds = GetFinishedGracePeriodOrders();
+
+ foreach (var orderId in orderIds)
+ {
+ Publish(orderId);
+ }
+ }
+
+ private IEnumerable GetFinishedGracePeriodOrders()
+ {
+ IEnumerable orderIds = new List();
+ using (var conn = new SqlConnection(_settings.ConnectionString))
+ {
+ if (conn != null)
+ {
+ conn.Open();
+ orderIds = conn.Query(
+ @"SELECT Id FROM [Microsoft.eShopOnContainers.Services.OrderingDb].[ordering].[orders]
+ WHERE DATEDIFF(hour, [OrderDate], GETDATE()) >= @GracePeriod
+ AND [OrderStatusId] = 1",
+ new { GracePeriod = _settings.GracePeriod });
+ }
+ }
+
+ return orderIds;
+ }
+
+ private void Publish(int orderId)
+ {
+ var confirmGracePeriodEvent = new ConfirmGracePeriodIntegrationEvent(orderId);
+
+ // Publish through the Event Bus and mark the saved event as published
+ _confirmGracePeriodEvent.PublishThroughEventBus(confirmGracePeriodEvent);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Services/SagaManager/SagaManager/appsettings.json b/src/Services/SagaManager/SagaManager/appsettings.json
new file mode 100644
index 000000000..19c7b8b28
--- /dev/null
+++ b/src/Services/SagaManager/SagaManager/appsettings.json
@@ -0,0 +1,11 @@
+{
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ },
+ "ConnectionString": "Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word"
+}