Merge new creation ordering workflow
This commit is contained in:
commit
152bc9fd01
@ -7,7 +7,11 @@ 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:
|
||||
|
||||
graceperiodmanager:
|
||||
environment:
|
||||
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
|
||||
- EventBusConnection=rabbitmq
|
||||
|
||||
basket.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
@ -109,6 +113,14 @@ services:
|
||||
ports:
|
||||
- "5107:80"
|
||||
|
||||
payment.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:5108
|
||||
- EventBusConnection=rabbitmq
|
||||
ports:
|
||||
- "5108:80"
|
||||
|
||||
locations.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
@ -118,4 +130,4 @@ services:
|
||||
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
|
||||
- EventBusConnection=rabbitmq
|
||||
ports:
|
||||
- "5109:80"
|
||||
- "5109:80"
|
||||
|
@ -122,6 +122,34 @@ services:
|
||||
labels:
|
||||
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
|
||||
|
||||
payment.api:
|
||||
image: eshop/payment.api:dev
|
||||
build:
|
||||
args:
|
||||
source: ${DOCKER_BUILD_SOURCE}
|
||||
environment:
|
||||
- DOTNET_USE_POLLING_FILE_WATCHER=1
|
||||
volumes:
|
||||
- ./src/Services/Payment/Payment.API:/app
|
||||
- ~/.nuget/packages:/root/.nuget/packages:ro
|
||||
- ~/clrdbg:/clrdbg:ro
|
||||
entrypoint: tail -f /dev/null
|
||||
labels:
|
||||
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
|
||||
|
||||
graceperiodmanager:
|
||||
image: eshop/graceperiodmanager:dev
|
||||
build:
|
||||
args:
|
||||
source: ${DOCKER_BUILD_SOURCE}
|
||||
volumes:
|
||||
- ./src/Services/GracePeriod/GracePeriodManager:/app
|
||||
- ~/.nuget/packages:/root/.nuget/packages:ro
|
||||
- ~/clrdbg:/clrdbg:ro
|
||||
entrypoint: tail -f /dev/null
|
||||
labels:
|
||||
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
|
||||
|
||||
locations.api:
|
||||
image: eshop/locations.api:dev
|
||||
build:
|
||||
|
@ -81,6 +81,26 @@ services:
|
||||
labels:
|
||||
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
|
||||
|
||||
payment.api:
|
||||
build:
|
||||
args:
|
||||
source: ${DOCKER_BUILD_SOURCE}
|
||||
volumes:
|
||||
- ~/clrdbg:/clrdbg:ro
|
||||
entrypoint: tail -f /dev/null
|
||||
labels:
|
||||
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
|
||||
|
||||
graceperiodmanager:
|
||||
build:
|
||||
args:
|
||||
source: ${DOCKER_BUILD_SOURCE}
|
||||
volumes:
|
||||
- ~/clrdbg:/clrdbg:ro
|
||||
entrypoint: tail -f /dev/null
|
||||
labels:
|
||||
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
|
||||
|
||||
locations.api:
|
||||
build:
|
||||
args:
|
||||
|
@ -1,6 +1,16 @@
|
||||
version: '2'
|
||||
|
||||
|
||||
services:
|
||||
graceperiodmanager:
|
||||
image: eshop/graceperiodmanager
|
||||
build:
|
||||
context: ./src/Services/GracePeriod/GracePeriodManager
|
||||
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
|
||||
|
||||
marketing.api:
|
||||
image: eshop/marketing.api
|
||||
@ -85,7 +95,15 @@ services:
|
||||
image: eshop/webstatus
|
||||
build:
|
||||
context: ./src/Web/WebStatus
|
||||
dockerfile: Dockerfile
|
||||
dockerfile: Dockerfile
|
||||
|
||||
payment.api:
|
||||
image: eshop/payment.api
|
||||
build:
|
||||
context: ./src/Services/Payment/Payment.API
|
||||
dockerfile: Dockerfile
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
|
||||
locations.api:
|
||||
image: locations.api
|
||||
@ -93,4 +111,4 @@ services:
|
||||
context: ./src/Services/Location/Locations.API
|
||||
dockerfile: Dockerfile
|
||||
depends_on:
|
||||
- nosql.data
|
||||
- nosql.data
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26430.12
|
||||
VisualStudioVersion = 15.0.26430.6
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
|
||||
EndProject
|
||||
@ -70,11 +70,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience",
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resilience.Http", "src\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj", "{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E145D-1593-47EE-9608-8E323D3C63F5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj", "{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.SqlServer", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj", "{4BD76717-3102-4969-8C2C-BAAA3F0263B6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBus.Tests", "src\BuildingBlocks\EventBus\EventBus.Tests\EventBus.Tests.csproj", "{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBus.Tests", "src\BuildingBlocks\EventBus\EventBus.Tests\EventBus.Tests.csproj", "{4A980AC4-7205-46BF-8CCB-09E44D700FD4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GracePeriod", "GracePeriod", "{F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GracePeriodManager", "src\Services\GracePeriod\GracePeriodManager\GracePeriodManager.csproj", "{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Location", "Location", "{41139F64-4046-4F16-96B7-D941D96FA9C6}"
|
||||
EndProject
|
||||
@ -870,6 +878,54 @@ Global
|
||||
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{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
|
||||
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
@ -966,54 +1022,102 @@ Global
|
||||
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.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
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||
@ -1191,18 +1295,18 @@ Global
|
||||
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
|
||||
{FBF43D93-F2E7-4FF8-B4AB-186895949B88} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
|
||||
{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}
|
||||
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
||||
{4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
||||
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
|
||||
{4A980AC4-7205-46BF-8CCB-09E44D700FD4} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
|
||||
{F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
|
||||
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47} = {F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E}
|
||||
{41139F64-4046-4F16-96B7-D941D96FA9C6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF} = {41139F64-4046-4F16-96B7-D941D96FA9C6}
|
||||
{88B22DBB-AA8F-4290-A454-2C109352C345} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A} = {88B22DBB-AA8F-4290-A454-2C109352C345}
|
||||
{A5260DE0-1FDD-467E-9CC1-A028AB081CEE} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
|
||||
{DF395F85-B010-465D-857A-7EBCC512C0C2} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE}
|
||||
{88B22DBB-AA8F-4290-A454-2C109352C345} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
|
||||
{23A33F9B-7672-426D-ACF9-FF8436ADC81A} = {88B22DBB-AA8F-4290-A454-2C109352C345}
|
||||
{41139F64-4046-4F16-96B7-D941D96FA9C6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
|
||||
{E7581357-FC34-474C-B8F5-307EE3CE05EF} = {41139F64-4046-4F16-96B7-D941D96FA9C6}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.4</TargetFramework>
|
||||
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.CommandBus</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
13
src/BuildingBlocks/CommandBus/CommandBus/ICommandBus.cs
Normal file
13
src/BuildingBlocks/CommandBus/CommandBus/ICommandBus.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
|
||||
{
|
||||
public interface ICommandBus
|
||||
{
|
||||
Task SendAsync<T>(T command) where T : IntegrationCommand;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
|
||||
{
|
||||
public abstract class IntegrationCommand
|
||||
{
|
||||
public Guid Id { get; private set; }
|
||||
public DateTime Sent { get; private set; }
|
||||
|
||||
protected IntegrationCommand()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
Sent = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.4</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
8
src/BuildingBlocks/EventBus/CommandBus/CommandBus.csproj
Normal file
8
src/BuildingBlocks/EventBus/CommandBus/CommandBus.csproj
Normal file
@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.CommandBus</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
16
src/BuildingBlocks/EventBus/CommandBus/ICommandBus.cs
Normal file
16
src/BuildingBlocks/EventBus/CommandBus/ICommandBus.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
|
||||
{
|
||||
public interface ICommandBus
|
||||
{
|
||||
void Send<T>(string name, T data);
|
||||
void Handle<TC>(string name, IIntegrationCommandHandler<TC> handler);
|
||||
void Handle(string name, IIntegrationCommandHandler handler);
|
||||
void Handle<TI, TC>(TI handler)
|
||||
where TI : IIntegrationCommandHandler<TC>;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
|
||||
{
|
||||
public interface IIntegrationCommandHandler
|
||||
{
|
||||
void Handle(IntegrationCommand command);
|
||||
}
|
||||
|
||||
public interface IIntegrationCommandHandler<T> : IIntegrationCommandHandler
|
||||
{
|
||||
void Handle(IntegrationCommand<T> command);
|
||||
}
|
||||
}
|
35
src/BuildingBlocks/EventBus/CommandBus/IntegrationCommand.cs
Normal file
35
src/BuildingBlocks/EventBus/CommandBus/IntegrationCommand.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
|
||||
{
|
||||
public abstract class IntegrationCommand
|
||||
{
|
||||
public Guid Id { get; }
|
||||
public DateTime Sent { get; }
|
||||
|
||||
public abstract object GetDataAsObject();
|
||||
|
||||
protected IntegrationCommand()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
Sent = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class IntegrationCommand<T> : IntegrationCommand
|
||||
{
|
||||
public T Data { get; }
|
||||
public string Name { get; }
|
||||
public override object GetDataAsObject() => Data;
|
||||
|
||||
public IntegrationCommand(string name, T data) : base()
|
||||
{
|
||||
Data = data;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,7 +18,7 @@ namespace EventBus.Tests
|
||||
public void After_One_Event_Subscription_Should_Contain_The_Event()
|
||||
{
|
||||
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||
manager.AddSubscription<TestIntegrationEvent,TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
|
||||
manager.AddSubscription<TestIntegrationEvent,TestIntegrationEventHandler>();
|
||||
Assert.True(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ namespace EventBus.Tests
|
||||
public void After_All_Subscriptions_Are_Deleted_Event_Should_No_Longer_Exists()
|
||||
{
|
||||
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
|
||||
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
|
||||
manager.RemoveSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
|
||||
Assert.False(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
|
||||
}
|
||||
@ -37,7 +37,7 @@ namespace EventBus.Tests
|
||||
bool raised = false;
|
||||
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||
manager.OnEventRemoved += (o, e) => raised = true;
|
||||
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
|
||||
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
|
||||
manager.RemoveSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
|
||||
Assert.True(raised);
|
||||
}
|
||||
@ -46,8 +46,8 @@ namespace EventBus.Tests
|
||||
public void Get_Handlers_For_Event_Should_Return_All_Handlers()
|
||||
{
|
||||
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
|
||||
manager.AddSubscription<TestIntegrationEvent, TestIntegrationOtherEventHandler>(() => new TestIntegrationOtherEventHandler());
|
||||
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
|
||||
manager.AddSubscription<TestIntegrationEvent, TestIntegrationOtherEventHandler>();
|
||||
var handlers = manager.GetHandlersForEvent<TestIntegrationEvent>();
|
||||
Assert.Equal(2, handlers.Count());
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
|
||||
{
|
||||
public interface IDynamicIntegrationEventHandler
|
||||
{
|
||||
Task Handle(dynamic eventData);
|
||||
}
|
||||
}
|
@ -5,9 +5,15 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
|
||||
{
|
||||
public interface IEventBus
|
||||
{
|
||||
void Subscribe<T, TH>(Func<TH> handler)
|
||||
void Subscribe<T, TH>()
|
||||
where T : IntegrationEvent
|
||||
where TH : IIntegrationEventHandler<T>;
|
||||
void SubscribeDynamic<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler;
|
||||
|
||||
void UnsubscribeDynamic<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler;
|
||||
|
||||
void Unsubscribe<T, TH>()
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
where T : IntegrationEvent;
|
||||
|
@ -2,6 +2,7 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static Microsoft.eShopOnContainers.BuildingBlocks.EventBus.InMemoryEventBusSubscriptionsManager;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
|
||||
{
|
||||
@ -9,18 +10,25 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
|
||||
{
|
||||
bool IsEmpty { get; }
|
||||
event EventHandler<string> OnEventRemoved;
|
||||
void AddSubscription<T, TH>(Func<TH> handler)
|
||||
void AddDynamicSubscription<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler;
|
||||
|
||||
void AddSubscription<T, TH>()
|
||||
where T : IntegrationEvent
|
||||
where TH : IIntegrationEventHandler<T>;
|
||||
|
||||
void RemoveSubscription<T, TH>()
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
where T : IntegrationEvent;
|
||||
void RemoveSubscription<T, TH>()
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
where T : IntegrationEvent;
|
||||
void RemoveDynamicSubscription<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler;
|
||||
|
||||
bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent;
|
||||
bool HasSubscriptionsForEvent(string eventName);
|
||||
Type GetEventTypeByName(string eventName);
|
||||
void Clear();
|
||||
IEnumerable<Delegate> GetHandlersForEvent<T>() where T : IntegrationEvent;
|
||||
IEnumerable<Delegate> GetHandlersForEvent(string eventName);
|
||||
IEnumerable<SubscriptionInfo> GetHandlersForEvent<T>() where T : IntegrationEvent;
|
||||
IEnumerable<SubscriptionInfo> GetHandlersForEvent(string eventName);
|
||||
string GetEventKey<T>();
|
||||
}
|
||||
}
|
@ -8,64 +8,106 @@ using System.Text;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
|
||||
{
|
||||
public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
|
||||
public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
|
||||
{
|
||||
private readonly Dictionary<string, List<Delegate>> _handlers;
|
||||
|
||||
|
||||
private readonly Dictionary<string, List<SubscriptionInfo>> _handlers;
|
||||
private readonly List<Type> _eventTypes;
|
||||
|
||||
public event EventHandler<string> OnEventRemoved;
|
||||
|
||||
public InMemoryEventBusSubscriptionsManager()
|
||||
{
|
||||
_handlers = new Dictionary<string, List<Delegate>>();
|
||||
_handlers = new Dictionary<string, List<SubscriptionInfo>>();
|
||||
_eventTypes = new List<Type>();
|
||||
}
|
||||
|
||||
public bool IsEmpty => !_handlers.Keys.Any();
|
||||
public void Clear() => _handlers.Clear();
|
||||
|
||||
public void AddSubscription<T, TH>(Func<TH> handler)
|
||||
public void AddDynamicSubscription<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler
|
||||
{
|
||||
DoAddSubscription(typeof(TH), eventName, isDynamic: true);
|
||||
}
|
||||
|
||||
public void AddSubscription<T, TH>()
|
||||
where T : IntegrationEvent
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
{
|
||||
var key = GetEventKey<T>();
|
||||
if (!HasSubscriptionsForEvent<T>())
|
||||
{
|
||||
_handlers.Add(key, new List<Delegate>());
|
||||
}
|
||||
_handlers[key].Add(handler);
|
||||
var eventName = GetEventKey<T>();
|
||||
DoAddSubscription(typeof(TH), eventName, isDynamic: false);
|
||||
_eventTypes.Add(typeof(T));
|
||||
}
|
||||
|
||||
private void DoAddSubscription(Type handlerType, string eventName, bool isDynamic)
|
||||
{
|
||||
if (!HasSubscriptionsForEvent(eventName))
|
||||
{
|
||||
_handlers.Add(eventName, new List<SubscriptionInfo>());
|
||||
}
|
||||
|
||||
if (_handlers[eventName].Any(s => s.HandlerType == handlerType))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Handler Type {handlerType.Name} already registered for '{eventName}'", nameof(handlerType));
|
||||
}
|
||||
|
||||
if (isDynamic)
|
||||
{
|
||||
_handlers[eventName].Add(SubscriptionInfo.Dynamic(handlerType));
|
||||
}
|
||||
else
|
||||
{
|
||||
_handlers[eventName].Add(SubscriptionInfo.Typed(handlerType));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void RemoveDynamicSubscription<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler
|
||||
{
|
||||
var handlerToRemove = FindDynamicSubscriptionToRemove<TH>(eventName);
|
||||
DoRemoveHandler(eventName, handlerToRemove);
|
||||
}
|
||||
|
||||
|
||||
public void RemoveSubscription<T, TH>()
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
where T : IntegrationEvent
|
||||
{
|
||||
var handlerToRemove = FindHandlerToRemove<T, TH>();
|
||||
if (handlerToRemove != null)
|
||||
var handlerToRemove = FindSubscriptionToRemove<T, TH>();
|
||||
var eventName = GetEventKey<T>();
|
||||
DoRemoveHandler(eventName, handlerToRemove);
|
||||
}
|
||||
|
||||
|
||||
private void DoRemoveHandler(string eventName, SubscriptionInfo subsToRemove)
|
||||
{
|
||||
if (subsToRemove != null)
|
||||
{
|
||||
var key = GetEventKey<T>();
|
||||
_handlers[key].Remove(handlerToRemove);
|
||||
if (!_handlers[key].Any())
|
||||
_handlers[eventName].Remove(subsToRemove);
|
||||
if (!_handlers[eventName].Any())
|
||||
{
|
||||
_handlers.Remove(key);
|
||||
var eventType = _eventTypes.SingleOrDefault(e => e.Name == key);
|
||||
_handlers.Remove(eventName);
|
||||
var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName);
|
||||
if (eventType != null)
|
||||
{
|
||||
_eventTypes.Remove(eventType);
|
||||
RaiseOnEventRemoved(eventType.Name);
|
||||
}
|
||||
RaiseOnEventRemoved(eventName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Delegate> GetHandlersForEvent<T>() where T : IntegrationEvent
|
||||
public IEnumerable<SubscriptionInfo> GetHandlersForEvent<T>() where T : IntegrationEvent
|
||||
{
|
||||
var key = GetEventKey<T>();
|
||||
return GetHandlersForEvent(key);
|
||||
}
|
||||
public IEnumerable<Delegate> GetHandlersForEvent(string eventName) => _handlers[eventName];
|
||||
public IEnumerable<SubscriptionInfo> GetHandlersForEvent(string eventName) => _handlers[eventName];
|
||||
|
||||
private void RaiseOnEventRemoved(string eventName)
|
||||
{
|
||||
@ -76,26 +118,31 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
|
||||
}
|
||||
}
|
||||
|
||||
private Delegate FindHandlerToRemove<T, TH>()
|
||||
where T : IntegrationEvent
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
|
||||
private SubscriptionInfo FindDynamicSubscriptionToRemove<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler
|
||||
{
|
||||
if (!HasSubscriptionsForEvent<T>())
|
||||
return DoFindSubscriptionToRemove(eventName, typeof(TH));
|
||||
}
|
||||
|
||||
|
||||
private SubscriptionInfo FindSubscriptionToRemove<T, TH>()
|
||||
where T : IntegrationEvent
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
{
|
||||
var eventName = GetEventKey<T>();
|
||||
return DoFindSubscriptionToRemove(eventName, typeof(TH));
|
||||
}
|
||||
|
||||
private SubscriptionInfo DoFindSubscriptionToRemove(string eventName, Type handlerType)
|
||||
{
|
||||
if (!HasSubscriptionsForEvent(eventName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var key = GetEventKey<T>();
|
||||
foreach (var func in _handlers[key])
|
||||
{
|
||||
var genericArgs = func.GetType().GetGenericArguments();
|
||||
if (genericArgs.SingleOrDefault() == typeof(TH))
|
||||
{
|
||||
return func;
|
||||
}
|
||||
}
|
||||
return _handlers[eventName].SingleOrDefault(s => s.HandlerType == handlerType);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent
|
||||
@ -104,10 +151,10 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
|
||||
return HasSubscriptionsForEvent(key);
|
||||
}
|
||||
public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName);
|
||||
|
||||
public Type GetEventTypeByName(string eventName) => _eventTypes.Single(t => t.Name == eventName);
|
||||
|
||||
private string GetEventKey<T>()
|
||||
public Type GetEventTypeByName(string eventName) => _eventTypes.SingleOrDefault(t => t.Name == eventName);
|
||||
|
||||
public string GetEventKey<T>()
|
||||
{
|
||||
return typeof(T).Name;
|
||||
}
|
||||
|
28
src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs
Normal file
28
src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
|
||||
{
|
||||
public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
|
||||
{
|
||||
public class SubscriptionInfo
|
||||
{
|
||||
public bool IsDynamic { get; }
|
||||
public Type HandlerType{ get; }
|
||||
|
||||
private SubscriptionInfo(bool isDynamic, Type handlerType)
|
||||
{
|
||||
IsDynamic = isDynamic;
|
||||
HandlerType = handlerType;
|
||||
}
|
||||
|
||||
public static SubscriptionInfo Dynamic(Type handlerType)
|
||||
{
|
||||
return new SubscriptionInfo(true, handlerType);
|
||||
}
|
||||
public static SubscriptionInfo Typed(Type handlerType)
|
||||
{
|
||||
return new SubscriptionInfo(false, handlerType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
//using Microsoft.eShopOnContainers.BuildingBlocks.CommandBus;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Polly;
|
||||
using Polly.Retry;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
using RabbitMQ.Client.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/*
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
{
|
||||
public class CommandBusRabbitMQ : ICommandBus, IDisposable
|
||||
{
|
||||
const string BROKER_NAME = "eshop_command_bus";
|
||||
|
||||
private readonly IRabbitMQPersistentConnection _persistentConnection;
|
||||
private readonly ILogger<CommandBusRabbitMQ> _logger;
|
||||
|
||||
private IModel _consumerChannel;
|
||||
private string _queueName;
|
||||
|
||||
private readonly Dictionary<string, IIntegrationCommandHandler> _handlers;
|
||||
private readonly Dictionary<string, Type> _typeMappings;
|
||||
|
||||
public CommandBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection,
|
||||
ILogger<CommandBusRabbitMQ> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_persistentConnection = persistentConnection;
|
||||
_handlers = new Dictionary<string, IIntegrationCommandHandler>();
|
||||
_typeMappings = new Dictionary<string, Type>();
|
||||
}
|
||||
|
||||
public void Send<T>(string name, T data)
|
||||
{
|
||||
Send(new IntegrationCommand<T>(name, data));
|
||||
}
|
||||
|
||||
public void Handle<TC>(string name, IIntegrationCommandHandler<TC> handler)
|
||||
{
|
||||
_handlers.Add(name, handler);
|
||||
_typeMappings.Add(name, typeof(TC));
|
||||
}
|
||||
|
||||
public void Handle(string name, IIntegrationCommandHandler handler)
|
||||
{
|
||||
_handlers.Add(name, handler);
|
||||
}
|
||||
public void Handle<TI, TC>(TI handler) where TI : IIntegrationCommandHandler<TC>
|
||||
{
|
||||
var name = typeof(TI).Name;
|
||||
_handlers.Add(name, handler);
|
||||
_typeMappings.Add(name, typeof(TC));
|
||||
}
|
||||
|
||||
private void Send<T>(IntegrationCommand<T> command)
|
||||
{
|
||||
if (!_persistentConnection.IsConnected)
|
||||
{
|
||||
_persistentConnection.TryConnect();
|
||||
}
|
||||
|
||||
var policy = RetryPolicy.Handle<BrokerUnreachableException>()
|
||||
.Or<SocketException>()
|
||||
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
|
||||
{
|
||||
_logger.LogWarning(ex.ToString());
|
||||
});
|
||||
|
||||
using (var channel = _persistentConnection.CreateModel())
|
||||
{
|
||||
var commandName = command.Name;
|
||||
channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct");
|
||||
var message = JsonConvert.SerializeObject(command);
|
||||
var body = Encoding.UTF8.GetBytes(message);
|
||||
policy.Execute(() =>
|
||||
{
|
||||
channel.BasicPublish(exchange: BROKER_NAME,
|
||||
routingKey: commandName,
|
||||
basicProperties: null,
|
||||
body: body);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private IModel CreateConsumerChannel()
|
||||
{
|
||||
if (!_persistentConnection.IsConnected)
|
||||
{
|
||||
_persistentConnection.TryConnect();
|
||||
}
|
||||
|
||||
var channel = _persistentConnection.CreateModel();
|
||||
|
||||
channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct");
|
||||
_queueName = channel.QueueDeclare().QueueName;
|
||||
var consumer = new EventingBasicConsumer(channel);
|
||||
consumer.Received += async (model, ea) =>
|
||||
{
|
||||
var commandName = ea.RoutingKey;
|
||||
var message = Encoding.UTF8.GetString(ea.Body);
|
||||
await InvokeHandler(commandName, message);
|
||||
};
|
||||
|
||||
channel.BasicConsume(queue: _queueName,
|
||||
noAck: true,
|
||||
consumer: consumer);
|
||||
|
||||
channel.CallbackException += (sender, ea) =>
|
||||
{
|
||||
_consumerChannel.Dispose();
|
||||
_consumerChannel = CreateConsumerChannel();
|
||||
};
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
private Task InvokeHandler(string commandName, string message)
|
||||
{
|
||||
if (_handlers.ContainsKey(commandName))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_consumerChannel != null)
|
||||
{
|
||||
_consumerChannel.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
@ -1,8 +1,10 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||
using Autofac;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Polly;
|
||||
using Polly.Retry;
|
||||
using RabbitMQ.Client;
|
||||
@ -25,17 +27,20 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
private readonly IRabbitMQPersistentConnection _persistentConnection;
|
||||
private readonly ILogger<EventBusRabbitMQ> _logger;
|
||||
private readonly IEventBusSubscriptionsManager _subsManager;
|
||||
|
||||
private readonly ILifetimeScope _autofac;
|
||||
private readonly string AUTOFAC_SCOPE_NAME = "eshop_event_bus";
|
||||
|
||||
private IModel _consumerChannel;
|
||||
private string _queueName;
|
||||
|
||||
public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger, IEventBusSubscriptionsManager subsManager)
|
||||
public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger,
|
||||
ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager)
|
||||
{
|
||||
_persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager();
|
||||
_consumerChannel = CreateConsumerChannel();
|
||||
_autofac = autofac;
|
||||
|
||||
_subsManager.OnEventRemoved += SubsManager_OnEventRemoved;
|
||||
}
|
||||
@ -96,12 +101,25 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe<T, TH>(Func<TH> handler)
|
||||
public void SubscribeDynamic<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler
|
||||
{
|
||||
DoInternalSubscription(eventName);
|
||||
_subsManager.AddDynamicSubscription<TH>(eventName);
|
||||
}
|
||||
|
||||
public void Subscribe<T, TH>()
|
||||
where T : IntegrationEvent
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
{
|
||||
var eventName = typeof(T).Name;
|
||||
var containsKey = _subsManager.HasSubscriptionsForEvent<T>();
|
||||
var eventName = _subsManager.GetEventKey<T>();
|
||||
DoInternalSubscription(eventName);
|
||||
_subsManager.AddSubscription<T, TH>();
|
||||
}
|
||||
|
||||
private void DoInternalSubscription(string eventName)
|
||||
{
|
||||
var containsKey = _subsManager.HasSubscriptionsForEvent(eventName);
|
||||
if (!containsKey)
|
||||
{
|
||||
if (!_persistentConnection.IsConnected)
|
||||
@ -116,9 +134,6 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
routingKey: eventName);
|
||||
}
|
||||
}
|
||||
|
||||
_subsManager.AddSubscription<T, TH>(handler);
|
||||
|
||||
}
|
||||
|
||||
public void Unsubscribe<T, TH>()
|
||||
@ -128,19 +143,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
_subsManager.RemoveSubscription<T, TH>();
|
||||
}
|
||||
|
||||
private static Func<IIntegrationEventHandler> FindHandlerByType(Type handlerType, IEnumerable<Func<IIntegrationEventHandler>> handlers)
|
||||
public void UnsubscribeDynamic<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler
|
||||
{
|
||||
foreach (var func in handlers)
|
||||
{
|
||||
if (func.GetMethodInfo().ReturnType == handlerType)
|
||||
{
|
||||
return func;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
_subsManager.RemoveDynamicSubscription<TH>(eventName);
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_consumerChannel != null)
|
||||
@ -190,17 +199,29 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
private async Task ProcessEvent(string eventName, string message)
|
||||
{
|
||||
|
||||
if (_subsManager.HasSubscriptionsForEvent(eventName))
|
||||
{
|
||||
var eventType = _subsManager.GetEventTypeByName(eventName);
|
||||
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
|
||||
var handlers = _subsManager.GetHandlersForEvent(eventName);
|
||||
|
||||
foreach (var handlerfactory in handlers)
|
||||
if (_subsManager.HasSubscriptionsForEvent(eventName))
|
||||
{
|
||||
using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME))
|
||||
{
|
||||
var handler = handlerfactory.DynamicInvoke();
|
||||
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
|
||||
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
|
||||
var subscriptions = _subsManager.GetHandlersForEvent(eventName);
|
||||
foreach (var subscription in subscriptions)
|
||||
{
|
||||
if (subscription.IsDynamic)
|
||||
{
|
||||
var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler;
|
||||
dynamic eventData = JObject.Parse(message);
|
||||
await handler.Handle(eventData);
|
||||
}
|
||||
else
|
||||
{
|
||||
var eventType = _subsManager.GetEventTypeByName(eventName);
|
||||
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
|
||||
var handler = scope.ResolveOptional(subscription.HandlerType);
|
||||
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
|
||||
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.5.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
<PackageReference Include="Polly" Version="5.1.0" />
|
||||
|
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace eShopOnContainers.Core.Models.Basket
|
||||
{
|
||||
public class BasketCheckout
|
||||
{
|
||||
[Required]
|
||||
public string City { get; set; }
|
||||
[Required]
|
||||
public string Street { get; set; }
|
||||
[Required]
|
||||
public string State { get; set; }
|
||||
[Required]
|
||||
public string Country { get; set; }
|
||||
|
||||
public string ZipCode { get; set; }
|
||||
[Required]
|
||||
public string CardNumber { get; set; }
|
||||
[Required]
|
||||
public string CardHolderName { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CardExpiration { get; set; }
|
||||
|
||||
[Required]
|
||||
public string CardSecurityNumber { get; set; }
|
||||
|
||||
public int CardTypeId { get; set; }
|
||||
|
||||
public string Buyer { get; set; }
|
||||
|
||||
[Required]
|
||||
public Guid RequestId { get; set; }
|
||||
}
|
||||
}
|
@ -42,6 +42,15 @@ namespace eShopOnContainers.Core.Services.Basket
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task CheckoutAsync(BasketCheckout basketCheckout, string token)
|
||||
{
|
||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint + "/checkout");
|
||||
|
||||
string uri = builder.ToString();
|
||||
|
||||
await _requestProvider.PostAsync(uri, basketCheckout, token);
|
||||
}
|
||||
|
||||
public async Task ClearBasketAsync(string guidUser, string token)
|
||||
{
|
||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);
|
||||
|
@ -7,6 +7,7 @@ namespace eShopOnContainers.Core.Services.Basket
|
||||
{
|
||||
Task<CustomerBasket> GetBasketAsync(string guidUser, string token);
|
||||
Task<CustomerBasket> UpdateBasketAsync(CustomerBasket customerBasket, string token);
|
||||
Task CheckoutAsync(BasketCheckout basketCheckout, string token);
|
||||
Task ClearBasketAsync(string guidUser, string token);
|
||||
}
|
||||
}
|
@ -1,13 +1,15 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using eShopOnContainers.Core.Models.Basket;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace eShopOnContainers.Core.Services.Order
|
||||
{
|
||||
public interface IOrderService
|
||||
{
|
||||
Task CreateOrderAsync(Models.Orders.Order newOrder, string token);
|
||||
//Task CreateOrderAsync(Models.Orders.Order newOrder, string token);
|
||||
Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token);
|
||||
Task<Models.Orders.Order> GetOrderAsync(int orderId, string token);
|
||||
Task<ObservableCollection<Models.Orders.CardType>> GetCardTypesAsync(string token);
|
||||
BasketCheckout MapOrderToBasket(Models.Orders.Order order);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using eShopOnContainers.Core.Extensions;
|
||||
using eShopOnContainers.Core.Models.Basket;
|
||||
using eShopOnContainers.Core.Models.Orders;
|
||||
using eShopOnContainers.Core.Models.User;
|
||||
using System;
|
||||
@ -64,17 +65,18 @@ namespace eShopOnContainers.Core.Services.Order
|
||||
new CardType { Id = 3, Name = "MasterCard" },
|
||||
};
|
||||
|
||||
public async Task CreateOrderAsync(Models.Orders.Order newOrder, string token)
|
||||
private static BasketCheckout MockBasketCheckout = new BasketCheckout()
|
||||
{
|
||||
await Task.Delay(500);
|
||||
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
newOrder.OrderNumber = string.Format("{0}", MockOrders.Count + 1);
|
||||
|
||||
MockOrders.Insert(0, newOrder);
|
||||
}
|
||||
}
|
||||
CardExpiration = DateTime.UtcNow,
|
||||
CardHolderName = "FakeCardHolderName",
|
||||
CardNumber = "122333423224",
|
||||
CardSecurityNumber = "1234",
|
||||
CardTypeId = 1,
|
||||
City = "FakeCity",
|
||||
Country = "FakeCountry",
|
||||
ZipCode = "FakeZipCode",
|
||||
Street = "FakeStreet"
|
||||
};
|
||||
|
||||
public async Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token)
|
||||
{
|
||||
@ -111,5 +113,10 @@ namespace eShopOnContainers.Core.Services.Order
|
||||
else
|
||||
return new ObservableCollection<CardType>();
|
||||
}
|
||||
|
||||
public BasketCheckout MapOrderToBasket(Models.Orders.Order order)
|
||||
{
|
||||
return MockBasketCheckout;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using eShopOnContainers.Core.Services.RequestProvider;
|
||||
using eShopOnContainers.Core.Models.Basket;
|
||||
using eShopOnContainers.Core.Services.RequestProvider;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
@ -14,17 +15,6 @@ namespace eShopOnContainers.Core.Services.Order
|
||||
_requestProvider = requestProvider;
|
||||
}
|
||||
|
||||
public async Task CreateOrderAsync(Models.Orders.Order newOrder, string token)
|
||||
{
|
||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.OrdersEndpoint);
|
||||
|
||||
builder.Path = "api/v1/orders/new";
|
||||
|
||||
string uri = builder.ToString();
|
||||
|
||||
await _requestProvider.PostAsync(uri, newOrder, token, "x-requestid");
|
||||
}
|
||||
|
||||
public async Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token)
|
||||
{
|
||||
|
||||
@ -82,5 +72,21 @@ namespace eShopOnContainers.Core.Services.Order
|
||||
return new ObservableCollection<Models.Orders.CardType>();
|
||||
}
|
||||
}
|
||||
|
||||
public BasketCheckout MapOrderToBasket(Models.Orders.Order order)
|
||||
{
|
||||
return new BasketCheckout()
|
||||
{
|
||||
CardExpiration = order.CardExpiration,
|
||||
CardHolderName = order.CardHolderName,
|
||||
CardNumber = order.CardNumber,
|
||||
CardSecurityNumber = order.CardSecurityNumber,
|
||||
CardTypeId = order.CardTypeId,
|
||||
City = order.ShippingCity,
|
||||
Country = order.ShippingCountry,
|
||||
ZipCode = order.ShippingZipCode,
|
||||
Street = order.ShippingStreet
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -132,8 +132,10 @@ namespace eShopOnContainers.Core.ViewModels
|
||||
{
|
||||
var authToken = Settings.AuthAccessToken;
|
||||
|
||||
// Create new order
|
||||
await _orderService.CreateOrderAsync(Order, authToken);
|
||||
var basket = _orderService.MapOrderToBasket(Order);
|
||||
|
||||
// Create basket checkout
|
||||
await _basketService.CheckoutAsync(basket, authToken);
|
||||
|
||||
// Clean Basket
|
||||
await _basketService.ClearBasketAsync(_shippingAddress.Id.ToString(), authToken);
|
||||
|
@ -64,6 +64,7 @@
|
||||
<Compile Include="Helpers\EasingHelper.cs" />
|
||||
<Compile Include="Helpers\ServicesHelper.cs" />
|
||||
<Compile Include="Helpers\Settings.cs" />
|
||||
<Compile Include="Models\Basket\BasketCheckout.cs" />
|
||||
<Compile Include="Models\Basket\BasketItem.cs" />
|
||||
<Compile Include="Models\Basket\CustomerBasket.cs" />
|
||||
<Compile Include="Models\Catalog\CatalogBrand.cs" />
|
||||
@ -262,6 +263,11 @@
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.ComponentModel.Annotations">
|
||||
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.6\Profile\Profile44\System.ComponentModel.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
|
@ -18,6 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
|
||||
<PackageReference Include="System.Threading" Version="4.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
|
||||
|
@ -5,6 +5,11 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Basket.API.IntegrationEvents.Events;
|
||||
using Microsoft.eShopOnContainers.Services.Basket.API.Services;
|
||||
using Basket.API.Model;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
|
||||
{
|
||||
@ -12,11 +17,17 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
|
||||
[Authorize]
|
||||
public class BasketController : Controller
|
||||
{
|
||||
private IBasketRepository _repository;
|
||||
private readonly IBasketRepository _repository;
|
||||
private readonly IIdentityService _identitySvc;
|
||||
private readonly IEventBus _eventBus;
|
||||
|
||||
public BasketController(IBasketRepository repository)
|
||||
public BasketController(IBasketRepository repository,
|
||||
IIdentityService identityService,
|
||||
IEventBus eventBus)
|
||||
{
|
||||
_repository = repository;
|
||||
_identitySvc = identityService;
|
||||
_eventBus = eventBus;
|
||||
}
|
||||
// GET /id
|
||||
[HttpGet("{id}")]
|
||||
@ -36,11 +47,38 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
|
||||
return Ok(basket);
|
||||
}
|
||||
|
||||
// DELETE /id
|
||||
[Route("checkout")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Checkout([FromBody]BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId)
|
||||
{
|
||||
var userId = _identitySvc.GetUserIdentity();
|
||||
basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ?
|
||||
guid : basketCheckout.RequestId;
|
||||
|
||||
var basket = await _repository.GetBasketAsync(userId);
|
||||
var eventMessage = new UserCheckoutAcceptedIntegrationEvent(userId, basketCheckout.City, basketCheckout.Street,
|
||||
basketCheckout.State, basketCheckout.Country, basketCheckout.ZipCode, basketCheckout.CardNumber, basketCheckout.CardHolderName,
|
||||
basketCheckout.CardExpiration, basketCheckout.CardSecurityNumber, basketCheckout.CardTypeId, basketCheckout.Buyer, basketCheckout.RequestId, basket);
|
||||
|
||||
// Once basket is checkout, sends an integration event to
|
||||
// ordering.api to convert basket to order and proceeds with
|
||||
// order creation process
|
||||
_eventBus.Publish(eventMessage);
|
||||
|
||||
if (basket == null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
return Accepted();
|
||||
}
|
||||
|
||||
// DELETE api/values/5
|
||||
[HttpDelete("{id}")]
|
||||
public void Delete(string id)
|
||||
{
|
||||
_repository.DeleteBasketAsync(id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Basket.API.IntegrationEvents.Events;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -7,9 +7,9 @@ namespace Basket.API.IntegrationEvents.Events
|
||||
// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems.
|
||||
public class OrderStartedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public string UserId { get; }
|
||||
public string UserId { get; set; }
|
||||
|
||||
public OrderStartedIntegrationEvent(string userId) =>
|
||||
UserId = userId;
|
||||
public OrderStartedIntegrationEvent(string userId)
|
||||
=> UserId = userId;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Basket.API.IntegrationEvents.Events
|
||||
{
|
||||
public class UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public string UserId { get; }
|
||||
|
||||
public int OrderNumber { get; set; }
|
||||
|
||||
public string City { get; set; }
|
||||
|
||||
public string Street { get; set; }
|
||||
|
||||
public string State { get; set; }
|
||||
|
||||
public string Country { get; set; }
|
||||
|
||||
public string ZipCode { get; set; }
|
||||
|
||||
public string CardNumber { get; set; }
|
||||
|
||||
public string CardHolderName { get; set; }
|
||||
|
||||
public DateTime CardExpiration { get; set; }
|
||||
|
||||
public string CardSecurityNumber { get; set; }
|
||||
|
||||
public int CardTypeId { get; set; }
|
||||
|
||||
public string Buyer { get; set; }
|
||||
|
||||
public Guid RequestId { get; set; }
|
||||
|
||||
public CustomerBasket Basket { get; }
|
||||
|
||||
public UserCheckoutAcceptedIntegrationEvent(string userId, string city, string street,
|
||||
string state, string country, string zipCode, string cardNumber, string cardHolderName,
|
||||
DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId,
|
||||
CustomerBasket basket)
|
||||
{
|
||||
UserId = userId;
|
||||
City = city;
|
||||
Street = street;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipCode;
|
||||
CardNumber = cardNumber;
|
||||
CardHolderName = cardHolderName;
|
||||
CardExpiration = cardExpiration;
|
||||
CardSecurityNumber = cardSecurityNumber;
|
||||
CardTypeId = cardTypeId;
|
||||
Buyer = buyer;
|
||||
Basket = basket;
|
||||
RequestId = requestId;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
|
||||
public CustomerBasket(string customerId)
|
||||
{
|
||||
BuyerId = customerId;
|
||||
Items = new List<Model.BasketItem>();
|
||||
Items = new List<BasketItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
35
src/Services/Basket/Basket.API/Model/BasketCheckout.cs
Normal file
35
src/Services/Basket/Basket.API/Model/BasketCheckout.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Basket.API.Model
|
||||
{
|
||||
public class BasketCheckout
|
||||
{
|
||||
public string City { get; set; }
|
||||
|
||||
public string Street { get; set; }
|
||||
|
||||
public string State { get; set; }
|
||||
|
||||
public string Country { get; set; }
|
||||
|
||||
public string ZipCode { get; set; }
|
||||
|
||||
public string CardNumber { get; set; }
|
||||
|
||||
public string CardHolderName { get; set; }
|
||||
|
||||
public DateTime CardExpiration { get; set; }
|
||||
|
||||
public string CardSecurityNumber { get; set; }
|
||||
|
||||
public int CardTypeId { get; set; }
|
||||
|
||||
public string Buyer { get; set; }
|
||||
|
||||
public Guid RequestId { get; set; }
|
||||
}
|
||||
}
|
||||
|
12
src/Services/Basket/Basket.API/Services/IIdentityService.cs
Normal file
12
src/Services/Basket/Basket.API/Services/IIdentityService.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Services
|
||||
{
|
||||
public interface IIdentityService
|
||||
{
|
||||
string GetUserIdentity();
|
||||
}
|
||||
}
|
24
src/Services/Basket/Basket.API/Services/IdentityService.cs
Normal file
24
src/Services/Basket/Basket.API/Services/IdentityService.cs
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Services
|
||||
{
|
||||
public class IdentityService : IIdentityService
|
||||
{
|
||||
private IHttpContextAccessor _context;
|
||||
|
||||
public IdentityService(IHttpContextAccessor context)
|
||||
{
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
public string GetUserIdentity()
|
||||
{
|
||||
return _context.HttpContext.User.FindFirst("sub").Value;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
using Basket.API.Infrastructure.Filters;
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Basket.API.Infrastructure.Filters;
|
||||
using Basket.API.IntegrationEvents.EventHandling;
|
||||
using Basket.API.IntegrationEvents.Events;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
|
||||
@ -10,6 +13,7 @@ using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server;
|
||||
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling;
|
||||
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events;
|
||||
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
|
||||
using Microsoft.eShopOnContainers.Services.Basket.API.Services;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.HealthChecks;
|
||||
@ -17,10 +21,10 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using RabbitMQ.Client;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||
{
|
||||
@ -39,7 +43,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
|
||||
services.AddHealthChecks(checks =>
|
||||
@ -102,9 +106,15 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||
.AllowAnyHeader()
|
||||
.AllowCredentials());
|
||||
});
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
services.AddTransient<IBasketRepository, RedisBasketRepository>();
|
||||
services.AddTransient<IIdentityService, IdentityService>();
|
||||
RegisterServiceBus(services);
|
||||
services.AddOptions();
|
||||
|
||||
var container = new ContainerBuilder();
|
||||
container.Populate(services);
|
||||
return new AutofacServiceProvider(container.Build());
|
||||
}
|
||||
|
||||
private void RegisterServiceBus(IServiceCollection services)
|
||||
@ -114,7 +124,6 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||
|
||||
services.AddTransient<ProductPriceChangedIntegrationEventHandler>();
|
||||
services.AddTransient<OrderStartedIntegrationEventHandler>();
|
||||
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
@ -155,19 +164,9 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||
|
||||
protected virtual void ConfigureEventBus(IApplicationBuilder app)
|
||||
{
|
||||
var catalogPriceHandler = app.ApplicationServices
|
||||
.GetService<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>>();
|
||||
|
||||
var orderStartedHandler = app.ApplicationServices
|
||||
.GetService<IIntegrationEventHandler<OrderStartedIntegrationEvent>>();
|
||||
|
||||
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
|
||||
|
||||
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>
|
||||
(() => app.ApplicationServices.GetRequiredService<ProductPriceChangedIntegrationEventHandler>());
|
||||
|
||||
eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>
|
||||
(() => app.ApplicationServices.GetRequiredService<OrderStartedIntegrationEventHandler>());
|
||||
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>();
|
||||
eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>1.1.2</RuntimeFrameworkVersion>
|
||||
<DebugType>portable</DebugType>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<AssemblyName>Catalog.API</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>Catalog.API</PackageId>
|
||||
<UserSecretsId>aspnet-Catalog.API-20161122013618</UserSecretsId>
|
||||
<RuntimeFrameworkVersion>1.1.2</RuntimeFrameworkVersion>
|
||||
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
|
||||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
</PropertyGroup>
|
||||
@ -25,12 +29,12 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.2" />
|
||||
@ -46,7 +50,6 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
|
||||
<PackageReference Include="Polly" Version="5.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -70,18 +70,18 @@
|
||||
{
|
||||
return new List<CatalogItem>()
|
||||
{
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1" },
|
||||
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2" },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3" },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4" },
|
||||
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5" },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6" },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7" },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8" },
|
||||
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup<T> White Mug", Name = "Cup<T> White Mug", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9" },
|
||||
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10" },
|
||||
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup<T> Sheet", Name = "Cup<T> Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11" },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/12" }
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1", AvailableStock = 1},
|
||||
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup<T> White Mug", Name = "Cup<T> White Mug", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup<T> Sheet", Name = "Cup<T> Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11", AvailableStock = 1 },
|
||||
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/12", AvailableStock = 1 }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
|
||||
|
||||
namespace Catalog.API.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(CatalogContext))]
|
||||
[Migration("20170509130025_AddStockProductItem")]
|
||||
partial class AddStockProductItem
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "1.1.1")
|
||||
.HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'")
|
||||
.HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'")
|
||||
.HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_brand_hilo")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<string>("Brand")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CatalogBrand");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_hilo")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<int>("AvailableStock");
|
||||
|
||||
b.Property<int>("CatalogBrandId");
|
||||
|
||||
b.Property<int>("CatalogTypeId");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<int>("MaxStockThreshold");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<bool>("OnReorder");
|
||||
|
||||
b.Property<string>("PictureUri");
|
||||
|
||||
b.Property<decimal>("Price");
|
||||
|
||||
b.Property<int>("RestockThreshold");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CatalogBrandId");
|
||||
|
||||
b.HasIndex("CatalogTypeId");
|
||||
|
||||
b.ToTable("Catalog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_type_hilo")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CatalogType");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand")
|
||||
.WithMany()
|
||||
.HasForeignKey("CatalogBrandId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType")
|
||||
.WithMany()
|
||||
.HasForeignKey("CatalogTypeId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Catalog.API.Infrastructure.Migrations
|
||||
{
|
||||
public partial class AddStockProductItem : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AvailableStock",
|
||||
table: "Catalog",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "MaxStockThreshold",
|
||||
table: "Catalog",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "OnReorder",
|
||||
table: "Catalog",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "RestockThreshold",
|
||||
table: "Catalog",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AvailableStock",
|
||||
table: "Catalog");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MaxStockThreshold",
|
||||
table: "Catalog");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OnReorder",
|
||||
table: "Catalog");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RestockThreshold",
|
||||
table: "Catalog");
|
||||
}
|
||||
}
|
||||
}
|
@ -42,20 +42,28 @@ namespace Catalog.API.Infrastructure.Migrations
|
||||
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_hilo")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
|
||||
|
||||
b.Property<int>("AvailableStock");
|
||||
|
||||
b.Property<int>("CatalogBrandId");
|
||||
|
||||
b.Property<int>("CatalogTypeId");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<int>("MaxStockThreshold");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<bool>("OnReorder");
|
||||
|
||||
b.Property<string>("PictureUri");
|
||||
|
||||
b.Property<decimal>("Price");
|
||||
|
||||
b.Property<int>("RestockThreshold");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CatalogBrandId");
|
||||
|
@ -0,0 +1,46 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling
|
||||
{
|
||||
using BuildingBlocks.EventBus.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.EventBus.Events;
|
||||
using Infrastructure;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using global::Catalog.API.IntegrationEvents;
|
||||
using IntegrationEvents.Events;
|
||||
|
||||
public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler :
|
||||
IIntegrationEventHandler<OrderStatusChangedToAwaitingValidationIntegrationEvent>
|
||||
{
|
||||
private readonly CatalogContext _catalogContext;
|
||||
private readonly ICatalogIntegrationEventService _catalogIntegrationEventService;
|
||||
|
||||
public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(CatalogContext catalogContext,
|
||||
ICatalogIntegrationEventService catalogIntegrationEventService)
|
||||
{
|
||||
_catalogContext = catalogContext;
|
||||
_catalogIntegrationEventService = catalogIntegrationEventService;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent command)
|
||||
{
|
||||
var confirmedOrderStockItems = new List<ConfirmedOrderStockItem>();
|
||||
|
||||
foreach (var orderStockItem in command.OrderStockItems)
|
||||
{
|
||||
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
|
||||
var hasStock = catalogItem.AvailableStock >= orderStockItem.Units;
|
||||
var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock);
|
||||
|
||||
confirmedOrderStockItems.Add(confirmedOrderStockItem);
|
||||
}
|
||||
|
||||
var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock)
|
||||
? (IntegrationEvent) new OrderStockRejectedIntegrationEvent(command.OrderId, confirmedOrderStockItems)
|
||||
: new OrderStockConfirmedIntegrationEvent(command.OrderId);
|
||||
|
||||
await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);
|
||||
await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling
|
||||
{
|
||||
using BuildingBlocks.EventBus.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Infrastructure;
|
||||
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events;
|
||||
|
||||
public class OrderStatusChangedToPaidIntegrationEventHandler :
|
||||
IIntegrationEventHandler<OrderStatusChangedToPaidIntegrationEvent>
|
||||
{
|
||||
private readonly CatalogContext _catalogContext;
|
||||
|
||||
public OrderStatusChangedToPaidIntegrationEventHandler(CatalogContext catalogContext)
|
||||
{
|
||||
_catalogContext = catalogContext;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderStatusChangedToPaidIntegrationEvent command)
|
||||
{
|
||||
//we're not blocking stock/inventory
|
||||
foreach (var orderStockItem in command.OrderStockItems)
|
||||
{
|
||||
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
|
||||
|
||||
catalogItem.RemoveStock(orderStockItem.Units);
|
||||
}
|
||||
|
||||
await _catalogContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
|
||||
{
|
||||
using BuildingBlocks.EventBus.Events;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
public IEnumerable<OrderStockItem> OrderStockItems { get; }
|
||||
|
||||
public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId,
|
||||
IEnumerable<OrderStockItem> orderStockItems)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderStockItems = orderStockItems;
|
||||
}
|
||||
}
|
||||
|
||||
public class OrderStockItem
|
||||
{
|
||||
public int ProductId { get; }
|
||||
public int Units { get; }
|
||||
|
||||
public OrderStockItem(int productId, int units)
|
||||
{
|
||||
ProductId = productId;
|
||||
Units = units;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
public IEnumerable<OrderStockItem> OrderStockItems { get; }
|
||||
|
||||
public OrderStatusChangedToPaidIntegrationEvent(int orderId,
|
||||
IEnumerable<OrderStockItem> orderStockItems)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderStockItems = orderStockItems;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
|
||||
{
|
||||
using BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class OrderStockConfirmedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
|
||||
{
|
||||
using BuildingBlocks.EventBus.Events;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class OrderStockRejectedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public List<ConfirmedOrderStockItem> OrderStockItems { get; }
|
||||
|
||||
public OrderStockRejectedIntegrationEvent(int orderId,
|
||||
List<ConfirmedOrderStockItem> orderStockItems)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderStockItems = orderStockItems;
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfirmedOrderStockItem
|
||||
{
|
||||
public int ProductId { get; }
|
||||
public bool HasStock { get; }
|
||||
|
||||
public ConfirmedOrderStockItem(int productId, bool hasStock)
|
||||
{
|
||||
ProductId = productId;
|
||||
HasStock = hasStock;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
|
||||
{
|
||||
using 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.
|
||||
@ -23,4 +20,4 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Eve
|
||||
OldPrice = oldPrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Catalog.API.Infrastructure.Exceptions;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model
|
||||
{
|
||||
@ -22,6 +23,79 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model
|
||||
|
||||
public CatalogBrand CatalogBrand { get; set; }
|
||||
|
||||
public CatalogItem() { }
|
||||
// Quantity in stock
|
||||
public int AvailableStock { get; set; }
|
||||
|
||||
// Available stock at which we should reorder
|
||||
public int RestockThreshold { get; set; }
|
||||
|
||||
|
||||
// Maximum number of units that can be in-stock at any time (due to physicial/logistical constraints in warehouses)
|
||||
public int MaxStockThreshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if item is on reorder
|
||||
/// </summary>
|
||||
public bool OnReorder { get; set; }
|
||||
|
||||
public CatalogItem() { }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decrements the quantity of a particular item in inventory and ensures the restockThreshold hasn't
|
||||
/// been breached. If so, a RestockRequest is generated in CheckThreshold.
|
||||
///
|
||||
/// If there is sufficient stock of an item, then the integer returned at the end of this call should be the same as quantityDesired.
|
||||
/// In the event that there is not sufficient stock available, the method will remove whatever stock is available and return that quantity to the client.
|
||||
/// In this case, it is the responsibility of the client to determine if the amount that is returned is the same as quantityDesired.
|
||||
/// It is invalid to pass in a negative number.
|
||||
/// </summary>
|
||||
/// <param name="quantityDesired"></param>
|
||||
/// <returns>int: Returns the number actually removed from stock. </returns>
|
||||
///
|
||||
public int RemoveStock(int quantityDesired)
|
||||
{
|
||||
if (AvailableStock == 0)
|
||||
{
|
||||
throw new CatalogDomainException($"Empty stock, product item {Name} is sold out");
|
||||
}
|
||||
|
||||
if (quantityDesired <= 0)
|
||||
{
|
||||
throw new CatalogDomainException($"Item units desired should be greater than cero");
|
||||
}
|
||||
|
||||
int removed = Math.Min(quantityDesired, this.AvailableStock);
|
||||
|
||||
this.AvailableStock -= removed;
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments the quantity of a particular item in inventory.
|
||||
/// <param name="quantity"></param>
|
||||
/// <returns>int: Returns the quantity that has been added to stock</returns>
|
||||
/// </summary>
|
||||
public int AddStock(int quantity)
|
||||
{
|
||||
int original = this.AvailableStock;
|
||||
|
||||
// The quantity that the client is trying to add to stock is greater than what can be physically accommodated in the Warehouse
|
||||
if ((this.AvailableStock + quantity) > this.MaxStockThreshold)
|
||||
{
|
||||
// For now, this method only adds new units up maximum stock threshold. In an expanded version of this application, we
|
||||
//could include tracking for the remaining units and store information about overstock elsewhere.
|
||||
this.AvailableStock += (this.MaxStockThreshold - this.AvailableStock);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.AvailableStock += quantity;
|
||||
}
|
||||
|
||||
this.OnReorder = false;
|
||||
|
||||
return this.AvailableStock - original;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Catalog.API
|
||||
{
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using global::Catalog.API.Infrastructure.Filters;
|
||||
using global::Catalog.API.IntegrationEvents;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -12,6 +14,8 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
|
||||
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
|
||||
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling;
|
||||
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.HealthChecks;
|
||||
@ -46,7 +50,7 @@
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Add framework services.
|
||||
|
||||
@ -122,8 +126,11 @@
|
||||
return new DefaultRabbitMQPersistentConnection(factory, logger);
|
||||
});
|
||||
|
||||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
|
||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
||||
RegisterServiceBus(services);
|
||||
|
||||
var container = new ContainerBuilder();
|
||||
container.Populate(services);
|
||||
return new AutofacServiceProvider(container.Build());
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||
@ -148,6 +155,8 @@
|
||||
|
||||
WaitForSqlAvailabilityAsync(context, loggerFactory, app).Wait();
|
||||
|
||||
ConfigureEventBus(app);
|
||||
|
||||
var integrationEventLogContext = new IntegrationEventLogContext(
|
||||
new DbContextOptionsBuilder<IntegrationEventLogContext>()
|
||||
.UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API"))
|
||||
@ -179,5 +188,26 @@
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void RegisterServiceBus(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
|
||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
||||
|
||||
services.AddTransient<IIntegrationEventHandler<OrderStatusChangedToAwaitingValidationIntegrationEvent>,
|
||||
OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
|
||||
services.AddTransient<IIntegrationEventHandler<OrderStatusChangedToPaidIntegrationEvent>,
|
||||
OrderStatusChangedToPaidIntegrationEventHandler>();
|
||||
}
|
||||
|
||||
private void ConfigureEventBus(IApplicationBuilder app)
|
||||
{
|
||||
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
|
||||
|
||||
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent,
|
||||
IIntegrationEventHandler<OrderStatusChangedToAwaitingValidationIntegrationEvent>>();
|
||||
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent,
|
||||
IIntegrationEventHandler<OrderStatusChangedToPaidIntegrationEvent>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
*
|
||||
!obj/Docker/publish/*
|
||||
!obj/Docker/empty/
|
5
src/Services/GracePeriod/GracePeriodManager/Dockerfile
Normal file
5
src/Services/GracePeriod/GracePeriodManager/Dockerfile
Normal file
@ -0,0 +1,5 @@
|
||||
FROM microsoft/aspnetcore:1.1.2
|
||||
ARG source
|
||||
WORKDIR /app
|
||||
COPY ${source:-obj/Docker/publish} .
|
||||
ENTRYPOINT ["dotnet", "GracePeriodManager.dll"]
|
@ -0,0 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||
<RuntimeFrameworkVersion>1.1.2</RuntimeFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
|
||||
<PackageReference Include="Dapper" Version="1.50.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
|
||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
|
||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
|
||||
<ProjectReference Include="..\..\Ordering\Ordering.Infrastructure\Ordering.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update=".dockerignore">
|
||||
<DependentUpon>Dockerfile</DependentUpon>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,11 @@
|
||||
namespace GracePeriodManager.IntegrationEvents.Events
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class GracePeriodConfirmedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get;}
|
||||
|
||||
public GracePeriodConfirmedIntegrationEvent(int orderId) => OrderId = orderId;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
namespace GracePeriodManager
|
||||
{
|
||||
public class ManagerSettings
|
||||
{
|
||||
public string ConnectionString { get; set; }
|
||||
|
||||
public string EventBusConnection { get; set; }
|
||||
|
||||
public int GracePeriodTime { get; set; }
|
||||
|
||||
public int CheckUpdateTime { get; set; }
|
||||
}
|
||||
}
|
94
src/Services/GracePeriod/GracePeriodManager/Program.cs
Normal file
94
src/Services/GracePeriod/GracePeriodManager/Program.cs
Normal file
@ -0,0 +1,94 @@
|
||||
namespace GracePeriodManager
|
||||
{
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Autofac;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||
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) => MainAsync().Wait();
|
||||
|
||||
static async Task MainAsync()
|
||||
{
|
||||
StartUp();
|
||||
|
||||
IServiceCollection services = new ServiceCollection();
|
||||
var serviceProvider = ConfigureServices(services);
|
||||
|
||||
var logger = serviceProvider.GetService<ILoggerFactory>();
|
||||
Configure(logger);
|
||||
|
||||
var gracePeriodManagerService = serviceProvider
|
||||
.GetRequiredService<IManagerService>();
|
||||
var checkUpdateTime = serviceProvider
|
||||
.GetRequiredService<IOptions<ManagerSettings>>().Value.CheckUpdateTime;
|
||||
|
||||
while (true)
|
||||
{
|
||||
gracePeriodManagerService.CheckConfirmedGracePeriodOrders();
|
||||
await Task.Delay(checkUpdateTime);
|
||||
}
|
||||
}
|
||||
|
||||
public static void StartUp()
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public static IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddLogging()
|
||||
.AddOptions()
|
||||
.Configure<ManagerSettings>(Configuration)
|
||||
.AddSingleton<IManagerService, ManagerService>()
|
||||
.AddSingleton<IRabbitMQPersistentConnection>(sp =>
|
||||
{
|
||||
var settings = sp.GetRequiredService<IOptions<ManagerSettings>>().Value;
|
||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
|
||||
var factory = new ConnectionFactory()
|
||||
{
|
||||
HostName = settings.EventBusConnection
|
||||
};
|
||||
|
||||
return new DefaultRabbitMQPersistentConnection(factory, logger);
|
||||
});
|
||||
|
||||
RegisterEventBus(services);
|
||||
|
||||
var container = new ContainerBuilder();
|
||||
container.Populate(services);
|
||||
return new AutofacServiceProvider(container.Build());
|
||||
}
|
||||
|
||||
public static void Configure(ILoggerFactory loggerFactory)
|
||||
{
|
||||
loggerFactory
|
||||
.AddConsole(Configuration.GetSection("Logging"))
|
||||
.AddConsole(LogLevel.Debug);
|
||||
}
|
||||
|
||||
private static void RegisterEventBus(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
||||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace GracePeriodManager.Services
|
||||
{
|
||||
public interface IManagerService
|
||||
{
|
||||
void CheckConfirmedGracePeriodOrders();
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
namespace GracePeriodManager.Services
|
||||
{
|
||||
using Dapper;
|
||||
using GracePeriodManager.IntegrationEvents.Events;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
public class ManagerService : IManagerService
|
||||
{
|
||||
private readonly ManagerSettings _settings;
|
||||
private readonly IEventBus _eventBus;
|
||||
private readonly ILogger<ManagerService> _logger;
|
||||
|
||||
public ManagerService(IOptions<ManagerSettings> settings,
|
||||
IEventBus eventBus,
|
||||
ILogger<ManagerService> logger)
|
||||
{
|
||||
_settings = settings.Value;
|
||||
_eventBus = eventBus;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void CheckConfirmedGracePeriodOrders()
|
||||
{
|
||||
var orderIds = GetConfirmedGracePeriodOrders();
|
||||
|
||||
foreach (var orderId in orderIds)
|
||||
{
|
||||
var confirmGracePeriodEvent = new GracePeriodConfirmedIntegrationEvent(orderId);
|
||||
_eventBus.Publish(confirmGracePeriodEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<int> GetConfirmedGracePeriodOrders()
|
||||
{
|
||||
IEnumerable<int> orderIds = new List<int>();
|
||||
using (var conn = new SqlConnection(_settings.ConnectionString))
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Grace Period Manager Client is trying to connect to database server");
|
||||
conn.Open();
|
||||
orderIds = conn.Query<int>(
|
||||
@"SELECT Id FROM [Microsoft.eShopOnContainers.Services.OrderingDb].[ordering].[orders]
|
||||
WHERE DATEDIFF(minute, [OrderDate], GETDATE()) >= @GracePeriodTime
|
||||
AND [OrderStatusId] = 1",
|
||||
new { GracePeriodTime = _settings.GracePeriodTime });
|
||||
}
|
||||
catch (SqlException exception)
|
||||
{
|
||||
_logger.LogCritical($"FATAL ERROR: Database connections could not be opened: {exception.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return orderIds;
|
||||
}
|
||||
}
|
||||
}
|
13
src/Services/GracePeriod/GracePeriodManager/appsettings.json
Normal file
13
src/Services/GracePeriod/GracePeriodManager/appsettings.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;",
|
||||
"GracePeriodTime": "1",
|
||||
"CheckUpdateTime": "30000"
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.2" />
|
||||
@ -39,6 +40,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="1.0.1" />
|
||||
<PackageReference Include="IdentityServer4.EntityFramework" Version="1.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
|
||||
|
@ -16,6 +16,9 @@ using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.HealthChecks;
|
||||
using Identity.API.Certificate;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Autofac;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -47,8 +50,9 @@ namespace eShopOnContainers.Identity
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
|
||||
// Add framework services.
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
|
||||
@ -99,6 +103,10 @@ namespace eShopOnContainers.Identity
|
||||
builder.UseSqlServer(connectionString, options =>
|
||||
options.MigrationsAssembly(migrationsAssembly)))
|
||||
.Services.AddTransient<IProfileService, ProfileService>();
|
||||
|
||||
var container = new ContainerBuilder();
|
||||
container.Populate(services);
|
||||
return new AutofacServiceProvider(container.Build());
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
@ -0,0 +1,21 @@
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Commands
|
||||
{
|
||||
public class CancelOrderCommand : IRequest<bool>
|
||||
{
|
||||
|
||||
[DataMember]
|
||||
public int OrderNumber { get; private set; }
|
||||
|
||||
public CancelOrderCommand(int orderNumber)
|
||||
{
|
||||
OrderNumber = orderNumber;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Commands
|
||||
{
|
||||
public class CancelOrderCommandIdentifiedHandler : IdentifierCommandHandler<CancelOrderCommand, bool>
|
||||
{
|
||||
public CancelOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for processing order.
|
||||
}
|
||||
}
|
||||
|
||||
public class CancelOrderCommandHandler : IAsyncRequestHandler<CancelOrderCommand, bool>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
|
||||
public CancelOrderCommandHandler(IOrderRepository orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler which processes the command when
|
||||
/// customer executes cancel order from app
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> Handle(CancelOrderCommand command)
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||
orderToUpdate.SetCancelledStatus();
|
||||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using MediatR;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Collections;
|
||||
using Ordering.API.Application.Models;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||
{
|
||||
@ -22,6 +23,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||
[DataMember]
|
||||
private readonly List<OrderItemDTO> _orderItems;
|
||||
|
||||
[DataMember]
|
||||
public string UserId { get; private set; }
|
||||
|
||||
[DataMember]
|
||||
public string City { get; private set; }
|
||||
|
||||
@ -52,12 +56,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||
[DataMember]
|
||||
public int CardTypeId { get; private set; }
|
||||
|
||||
[DataMember]
|
||||
public int PaymentId { get; private set; }
|
||||
|
||||
[DataMember]
|
||||
public int BuyerId { get; private set; }
|
||||
|
||||
[DataMember]
|
||||
public IEnumerable<OrderItemDTO> OrderItems => _orderItems;
|
||||
|
||||
@ -66,11 +64,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||
_orderItems = new List<OrderItemDTO>();
|
||||
}
|
||||
|
||||
public CreateOrderCommand(List<OrderItemDTO> orderItems, string city, string street, string state, string country, string zipcode,
|
||||
public CreateOrderCommand(List<BasketItem> basketItems, string userId, string city, string street, string state, string country, string zipcode,
|
||||
string cardNumber, string cardHolderName, DateTime cardExpiration,
|
||||
string cardSecurityNumber, int cardTypeId, int paymentId, int buyerId) : this()
|
||||
string cardSecurityNumber, int cardTypeId) : this()
|
||||
{
|
||||
_orderItems = orderItems;
|
||||
_orderItems = MapToOrderItems(basketItems);
|
||||
UserId = userId;
|
||||
City = city;
|
||||
Street = street;
|
||||
State = state;
|
||||
@ -82,8 +81,21 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||
CardSecurityNumber = cardSecurityNumber;
|
||||
CardTypeId = cardTypeId;
|
||||
CardExpiration = cardExpiration;
|
||||
PaymentId = paymentId;
|
||||
BuyerId = buyerId;
|
||||
}
|
||||
|
||||
private List<OrderItemDTO> MapToOrderItems(List<BasketItem> basketItems)
|
||||
{
|
||||
var result = new List<OrderItemDTO>();
|
||||
basketItems.ForEach((item) => {
|
||||
result.Add(new OrderItemDTO() {
|
||||
ProductId = int.TryParse(item.ProductId, out int id) ? id : -1,
|
||||
ProductName = item.ProductName,
|
||||
PictureUrl = item.PictureUrl,
|
||||
UnitPrice = item.UnitPrice,
|
||||
Units = item.Quantity
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public class OrderItemDTO
|
||||
|
@ -42,8 +42,8 @@
|
||||
// methods and constructor so validations, invariants and business logic
|
||||
// make sure that consistency is preserved across the whole aggregate
|
||||
var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode);
|
||||
var order = new Order(address , message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration);
|
||||
|
||||
var order = new Order(message.UserId, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration);
|
||||
|
||||
foreach (var item in message.OrderItems)
|
||||
{
|
||||
order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units);
|
||||
|
@ -0,0 +1,21 @@
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Commands
|
||||
{
|
||||
public class ShipOrderCommand : IRequest<bool>
|
||||
{
|
||||
|
||||
[DataMember]
|
||||
public int OrderNumber { get; private set; }
|
||||
|
||||
public ShipOrderCommand(int orderNumber)
|
||||
{
|
||||
OrderNumber = orderNumber;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Commands
|
||||
{
|
||||
public class ShipOrderCommandIdentifiedHandler : IdentifierCommandHandler<ShipOrderCommand, bool>
|
||||
{
|
||||
public ShipOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for processing order.
|
||||
}
|
||||
}
|
||||
|
||||
public class ShipOrderCommandHandler : IAsyncRequestHandler<ShipOrderCommand, bool>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
|
||||
public ShipOrderCommandHandler(IOrderRepository orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler which processes the command when
|
||||
/// administrator executes ship order from app
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> Handle(ShipOrderCommand command)
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||
orderToUpdate.SetShippedStatus();
|
||||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ordering.API.IntegrationEvents;
|
||||
using Ordering.API.IntegrationEvents.Events;
|
||||
using Ordering.API.Application.IntegrationEvents;
|
||||
using Ordering.API.Application.IntegrationEvents.Events;
|
||||
using Ordering.Domain.Events;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
@ -12,16 +12,13 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri
|
||||
public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler
|
||||
: IAsyncNotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
private readonly ILoggerFactory _logger;
|
||||
|
||||
public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(
|
||||
IOrderRepository orderRepository, ILoggerFactory logger,
|
||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||
IOrderRepository orderRepository, ILoggerFactory logger)
|
||||
{
|
||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
|
||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
@ -32,18 +29,7 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId);
|
||||
orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id);
|
||||
orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id);
|
||||
|
||||
var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(buyerPaymentMethodVerifiedEvent.Buyer.IdentityGuid);
|
||||
|
||||
// Using a local transaction to achieve atomicity between original Ordering database operation and
|
||||
// the IntegrationEventLog. Only saving event if order has been successfully persisted to db
|
||||
await _orderingIntegrationEventService
|
||||
.SaveEventAndOrderingContextChangesAsync(orderStartedIntegrationEvent);
|
||||
|
||||
// Publish ordering integration event and mark it as published
|
||||
await _orderingIntegrationEventService
|
||||
.PublishThroughEventBusAsync(orderStartedIntegrationEvent);
|
||||
orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id);
|
||||
|
||||
_logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler))
|
||||
.LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }");
|
||||
|
@ -0,0 +1,43 @@
|
||||
namespace Ordering.API.Application.DomainEventHandlers.OrderGracePeriodConfirmed
|
||||
{
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Domain.Events;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ordering.API.Application.IntegrationEvents;
|
||||
using System.Linq;
|
||||
using Ordering.API.Application.IntegrationEvents.Events;
|
||||
|
||||
public class OrderStatusChangedToAwaitingValidationDomainEventHandler
|
||||
: IAsyncNotificationHandler<OrderStatusChangedToAwaitingValidationDomainEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
private readonly ILoggerFactory _logger;
|
||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||
|
||||
public OrderStatusChangedToAwaitingValidationDomainEventHandler(
|
||||
IOrderRepository orderRepository, ILoggerFactory logger,
|
||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||
{
|
||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_orderingIntegrationEventService = orderingIntegrationEventService;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent)
|
||||
{
|
||||
_logger.CreateLogger(nameof(OrderStatusChangedToAwaitingValidationDomainEvent))
|
||||
.LogTrace($"Order with Id: {orderStatusChangedToAwaitingValidationDomainEvent.OrderId} has been successfully updated with " +
|
||||
$"a status order id: {OrderStatus.AwaitingValidation.Id}");
|
||||
|
||||
var orderStockList = orderStatusChangedToAwaitingValidationDomainEvent.OrderItems
|
||||
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));
|
||||
|
||||
var orderStatusChangedToAwaitingValidationIntegrationEvent = new OrderStatusChangedToAwaitingValidationIntegrationEvent(
|
||||
orderStatusChangedToAwaitingValidationDomainEvent.OrderId, orderStockList);
|
||||
await _orderingIntegrationEventService.PublishThroughEventBusAsync(orderStatusChangedToAwaitingValidationIntegrationEvent);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
namespace Ordering.API.Application.DomainEventHandlers.OrderPaid
|
||||
{
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Domain.Events;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ordering.API.Application.IntegrationEvents;
|
||||
using System.Linq;
|
||||
using Ordering.API.Application.IntegrationEvents.Events;
|
||||
|
||||
public class OrderStatusChangedToPaidDomainEventHandler
|
||||
: IAsyncNotificationHandler<OrderStatusChangedToPaidDomainEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
private readonly ILoggerFactory _logger;
|
||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||
|
||||
public OrderStatusChangedToPaidDomainEventHandler(
|
||||
IOrderRepository orderRepository, ILoggerFactory logger,
|
||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||
{
|
||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_orderingIntegrationEventService = orderingIntegrationEventService;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent)
|
||||
{
|
||||
_logger.CreateLogger(nameof(OrderStatusChangedToPaidDomainEventHandler))
|
||||
.LogTrace($"Order with Id: {orderStatusChangedToPaidDomainEvent.OrderId} has been successfully updated with " +
|
||||
$"a status order id: {OrderStatus.Paid.Id}");
|
||||
|
||||
var orderStockList = orderStatusChangedToPaidDomainEvent.OrderItems
|
||||
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));
|
||||
|
||||
var orderStatusChangedToPaidIntegrationEvent = new OrderStatusChangedToPaidIntegrationEvent(orderStatusChangedToPaidDomainEvent.OrderId,
|
||||
orderStockList);
|
||||
await _orderingIntegrationEventService.PublishThroughEventBusAsync(orderStatusChangedToPaidIntegrationEvent);
|
||||
}
|
||||
}
|
||||
}
|
@ -26,14 +26,12 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent
|
||||
{
|
||||
var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1;
|
||||
|
||||
var userGuid = _identityService.GetUserIdentity();
|
||||
|
||||
var buyer = await _buyerRepository.FindAsync(userGuid);
|
||||
var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId);
|
||||
bool buyerOriginallyExisted = (buyer == null) ? false : true;
|
||||
|
||||
if (!buyerOriginallyExisted)
|
||||
{
|
||||
buyer = new Buyer(userGuid);
|
||||
buyer = new Buyer(orderStartedEvent.UserId);
|
||||
}
|
||||
|
||||
buyer.VerifyOrAddPaymentMethod(cardTypeId,
|
||||
|
@ -0,0 +1,38 @@
|
||||
namespace Ordering.API.Application.DomainEventHandlers.OrderStockConfirmed
|
||||
{
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Domain.Events;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ordering.API.Application.IntegrationEvents;
|
||||
using Ordering.API.Application.IntegrationEvents.Events;
|
||||
|
||||
public class OrderStatusChangedToStockConfirmedDomainEventHandler
|
||||
: IAsyncNotificationHandler<OrderStatusChangedToStockConfirmedDomainEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
private readonly ILoggerFactory _logger;
|
||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||
|
||||
public OrderStatusChangedToStockConfirmedDomainEventHandler(
|
||||
IOrderRepository orderRepository, ILoggerFactory logger,
|
||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||
{
|
||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_orderingIntegrationEventService = orderingIntegrationEventService;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent)
|
||||
{
|
||||
_logger.CreateLogger(nameof(OrderStatusChangedToStockConfirmedDomainEventHandler))
|
||||
.LogTrace($"Order with Id: {orderStatusChangedToStockConfirmedDomainEvent.OrderId} has been successfully updated with " +
|
||||
$"a status order id: {OrderStatus.StockConfirmed.Id}");
|
||||
|
||||
var orderStatusChangedToStockConfirmedIntegrationEvent = new OrderStatusChangedToStockConfirmedIntegrationEvent(orderStatusChangedToStockConfirmedDomainEvent.OrderId);
|
||||
await _orderingIntegrationEventService.PublishThroughEventBusAsync(orderStatusChangedToStockConfirmedIntegrationEvent);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Ordering.API.Application.IntegrationEvents.Events;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.IntegrationEvents.EventHandling
|
||||
{
|
||||
public class GracePeriodConfirmedIntegrationEventHandler : IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
|
||||
public GracePeriodConfirmedIntegrationEventHandler(IOrderRepository orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler which confirms that the grace period
|
||||
/// has been completed and order will not initially be cancelled.
|
||||
/// Therefore, the order process continues for validation.
|
||||
/// </summary>
|
||||
/// <param name="event">
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public async Task Handle(GracePeriodConfirmedIntegrationEvent @event)
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
|
||||
orderToUpdate.SetAwaitingValidationStatus();
|
||||
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.EventHandling
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Ordering.API.Application.IntegrationEvents.Events;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class OrderPaymentFailedIntegrationEventHandler :
|
||||
IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
|
||||
public OrderPaymentFailedIntegrationEventHandler(IOrderRepository orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderPaymentFailedIntegrationEvent @event)
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
|
||||
|
||||
orderToUpdate.SetCancelledStatus();
|
||||
|
||||
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.EventHandling
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Ordering.API.Application.IntegrationEvents.Events;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class OrderPaymentSuccededIntegrationEventHandler :
|
||||
IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
|
||||
public OrderPaymentSuccededIntegrationEventHandler(IOrderRepository orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderPaymentSuccededIntegrationEvent @event)
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
|
||||
|
||||
orderToUpdate.SetPaidStatus();
|
||||
|
||||
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.EventHandling
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Events;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
|
||||
public class OrderStockConfirmedIntegrationEventHandler :
|
||||
IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
|
||||
public OrderStockConfirmedIntegrationEventHandler(IOrderRepository orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderStockConfirmedIntegrationEvent @event)
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
|
||||
|
||||
orderToUpdate.SetStockConfirmedStatus();
|
||||
|
||||
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.EventHandling
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Events;
|
||||
using System.Linq;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
|
||||
public class OrderStockRejectedIntegrationEventHandler : IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
|
||||
public OrderStockRejectedIntegrationEventHandler(IOrderRepository orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
public async Task Handle(OrderStockRejectedIntegrationEvent @event)
|
||||
{
|
||||
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
|
||||
|
||||
var orderStockRejectedItems = @event.OrderStockItems
|
||||
.FindAll(c => !c.HasStock)
|
||||
.Select(c => c.ProductId);
|
||||
|
||||
orderToUpdate.SetCancelledStatusWhenStockIsRejected(orderStockRejectedItems);
|
||||
|
||||
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ordering.API.Application.IntegrationEvents.Events;
|
||||
|
||||
namespace Ordering.API.Application.IntegrationEvents.EventHandling
|
||||
{
|
||||
public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
private readonly ILoggerFactory _logger;
|
||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||
|
||||
public UserCheckoutAcceptedIntegrationEventHandler(IMediator mediator,
|
||||
ILoggerFactory logger, IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||
{
|
||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integration event handler which starts the create order process
|
||||
/// </summary>
|
||||
/// <param name="eventMsg">
|
||||
/// Integration event message which is sent by the
|
||||
/// basket.api once it has successfully process the
|
||||
/// order items.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public async Task Handle(UserCheckoutAcceptedIntegrationEvent eventMsg)
|
||||
{
|
||||
var result = false;
|
||||
|
||||
// Send Integration event to clean basket once basket is converted to Order and before starting with the order creation process
|
||||
var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(eventMsg.UserId);
|
||||
await _orderingIntegrationEventService.PublishThroughEventBusAsync(orderStartedIntegrationEvent);
|
||||
|
||||
if (eventMsg.RequestId != Guid.Empty)
|
||||
{
|
||||
var createOrderCommand = new CreateOrderCommand(eventMsg.Basket.Items, eventMsg.UserId, eventMsg.City, eventMsg.Street,
|
||||
eventMsg.State, eventMsg.Country, eventMsg.ZipCode,
|
||||
eventMsg.CardNumber, eventMsg.CardHolderName, eventMsg.CardExpiration,
|
||||
eventMsg.CardSecurityNumber, eventMsg.CardTypeId);
|
||||
|
||||
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, eventMsg.RequestId);
|
||||
result = await _mediator.Send(requestCreateOrder);
|
||||
}
|
||||
|
||||
_logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler))
|
||||
.LogTrace(result ? $"UserCheckoutAccepted integration event has been received and a create new order process is started with requestId: {eventMsg.RequestId}" :
|
||||
$"UserCheckoutAccepted integration event has been received but a new order process has failed with requestId: {eventMsg.RequestId}");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class GracePeriodConfirmedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public GracePeriodConfirmedIntegrationEvent(int orderId) =>
|
||||
OrderId = orderId;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class OrderPaymentFailedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class OrderPaymentSuccededIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public OrderPaymentSuccededIntegrationEvent(int orderId) => OrderId = orderId;
|
||||
}
|
||||
}
|
@ -4,16 +4,16 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.IntegrationEvents.Events
|
||||
namespace Ordering.API.Application.IntegrationEvents.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 OrderStartedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public string UserId { get; }
|
||||
public string UserId { get; set; }
|
||||
|
||||
public OrderStartedIntegrationEvent(string userId) =>
|
||||
UserId = userId;
|
||||
public OrderStartedIntegrationEvent(string userId)
|
||||
=> UserId = userId;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
public IEnumerable<OrderStockItem> OrderStockItems { get; }
|
||||
|
||||
public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId,
|
||||
IEnumerable<OrderStockItem> orderStockItems)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderStockItems = orderStockItems;
|
||||
}
|
||||
}
|
||||
|
||||
public class OrderStockItem
|
||||
{
|
||||
public int ProductId { get; }
|
||||
public int Units { get; }
|
||||
|
||||
public OrderStockItem(int productId, int units)
|
||||
{
|
||||
ProductId = productId;
|
||||
Units = units;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
public IEnumerable<OrderStockItem> OrderStockItems { get; }
|
||||
|
||||
public OrderStatusChangedToPaidIntegrationEvent(int orderId,
|
||||
IEnumerable<OrderStockItem> orderStockItems)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderStockItems = orderStockItems;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId)
|
||||
=> OrderId = orderId;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
|
||||
public class OrderStockConfirmedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class OrderStockRejectedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public List<ConfirmedOrderStockItem> OrderStockItems { get; }
|
||||
|
||||
public OrderStockRejectedIntegrationEvent(int orderId,
|
||||
List<ConfirmedOrderStockItem> orderStockItems)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderStockItems = orderStockItems;
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfirmedOrderStockItem
|
||||
{
|
||||
public int ProductId { get; }
|
||||
public bool HasStock { get; }
|
||||
|
||||
public ConfirmedOrderStockItem(int productId, bool hasStock)
|
||||
{
|
||||
ProductId = productId;
|
||||
HasStock = hasStock;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using Ordering.API.Application.Models;
|
||||
using System;
|
||||
|
||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
||||
{
|
||||
public class UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
public string UserId { get; }
|
||||
|
||||
public string City { get; set; }
|
||||
|
||||
public string Street { get; set; }
|
||||
|
||||
public string State { get; set; }
|
||||
|
||||
public string Country { get; set; }
|
||||
|
||||
public string ZipCode { get; set; }
|
||||
|
||||
public string CardNumber { get; set; }
|
||||
|
||||
public string CardHolderName { get; set; }
|
||||
|
||||
public DateTime CardExpiration { get; set; }
|
||||
|
||||
public string CardSecurityNumber { get; set; }
|
||||
|
||||
public int CardTypeId { get; set; }
|
||||
|
||||
public string Buyer { get; set; }
|
||||
|
||||
public Guid RequestId { get; set; }
|
||||
|
||||
public CustomerBasket Basket { get; }
|
||||
|
||||
public UserCheckoutAcceptedIntegrationEvent(string userId, string city, string street,
|
||||
string state, string country, string zipCode, string cardNumber, string cardHolderName,
|
||||
DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId,
|
||||
CustomerBasket basket)
|
||||
{
|
||||
UserId = userId;
|
||||
City = city;
|
||||
Street = street;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipCode;
|
||||
CardNumber = cardNumber;
|
||||
CardHolderName = cardHolderName;
|
||||
CardExpiration = cardExpiration;
|
||||
CardSecurityNumber = cardSecurityNumber;
|
||||
CardTypeId = cardTypeId;
|
||||
Buyer = buyer;
|
||||
Basket = basket;
|
||||
RequestId = requestId;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,14 +1,10 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.IntegrationEvents
|
||||
namespace Ordering.API.Application.IntegrationEvents
|
||||
{
|
||||
public interface IOrderingIntegrationEventService
|
||||
{
|
||||
Task SaveEventAndOrderingContextChangesAsync(IntegrationEvent evt);
|
||||
Task PublishThroughEventBusAsync(IntegrationEvent evt);
|
||||
}
|
||||
}
|
@ -4,13 +4,13 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.IntegrationEvents
|
||||
namespace Ordering.API.Application.IntegrationEvents
|
||||
{
|
||||
public class OrderingIntegrationEventService : IOrderingIntegrationEventService
|
||||
{
|
||||
@ -19,7 +19,7 @@ namespace Ordering.API.IntegrationEvents
|
||||
private readonly OrderingContext _orderingContext;
|
||||
private readonly IIntegrationEventLogService _eventLogService;
|
||||
|
||||
public OrderingIntegrationEventService (IEventBus eventBus, OrderingContext orderingContext,
|
||||
public OrderingIntegrationEventService(IEventBus eventBus, OrderingContext orderingContext,
|
||||
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
|
||||
{
|
||||
_orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext));
|
||||
@ -30,11 +30,12 @@ namespace Ordering.API.IntegrationEvents
|
||||
|
||||
public async Task PublishThroughEventBusAsync(IntegrationEvent evt)
|
||||
{
|
||||
await SaveEventAndOrderingContextChangesAsync(evt);
|
||||
_eventBus.Publish(evt);
|
||||
await _eventLogService.MarkEventAsPublishedAsync(evt);
|
||||
}
|
||||
|
||||
public async Task SaveEventAndOrderingContextChangesAsync(IntegrationEvent evt)
|
||||
private async Task SaveEventAndOrderingContextChangesAsync(IntegrationEvent evt)
|
||||
{
|
||||
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
||||
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Models
|
||||
{
|
||||
public class BasketItem
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string ProductId { get; set; }
|
||||
public string ProductName { get; set; }
|
||||
public decimal UnitPrice { get; set; }
|
||||
public decimal OldUnitPrice { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public string PictureUrl { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Models
|
||||
{
|
||||
public class CustomerBasket
|
||||
{
|
||||
public string BuyerId { get; set; }
|
||||
public List<BasketItem> Items { get; set; }
|
||||
|
||||
public CustomerBasket(string customerId)
|
||||
{
|
||||
BuyerId = customerId;
|
||||
Items = new List<BasketItem>();
|
||||
}
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@
|
||||
connection.Open();
|
||||
|
||||
var result = await connection.QueryAsync<dynamic>(
|
||||
@"select o.[Id] as ordernumber,o.OrderDate as date, os.Name as status,
|
||||
@"select o.[Id] as ordernumber,o.OrderDate as date, o.Description as description, os.Name as status,
|
||||
oi.ProductName as productname, oi.Units as units, oi.UnitPrice as unitprice, oi.PictureUrl as pictureurl,
|
||||
a.Street as street, a.City as city, a.Country as country, a.State as state, a.ZipCode as zipcode
|
||||
FROM ordering.Orders o
|
||||
@ -75,6 +75,7 @@
|
||||
order.ordernumber = result[0].ordernumber;
|
||||
order.date = result[0].date;
|
||||
order.status = result[0].status;
|
||||
order.description = result[0].description;
|
||||
order.street = result[0].street;
|
||||
order.city = result[0].city;
|
||||
order.zipcode = result[0].zipcode;
|
||||
|
@ -0,0 +1,17 @@
|
||||
using FluentValidation;
|
||||
using Ordering.API.Application.Commands;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Validations
|
||||
{
|
||||
public class CancelOrderCommandValidator : AbstractValidator<CancelOrderCommand>
|
||||
{
|
||||
public CancelOrderCommandValidator()
|
||||
{
|
||||
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using FluentValidation;
|
||||
using Ordering.API.Application.Commands;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Validations
|
||||
{
|
||||
public class ShipOrderCommandValidator : AbstractValidator<ShipOrderCommand>
|
||||
{
|
||||
public ShipOrderCommandValidator()
|
||||
{
|
||||
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
|
||||
using Ordering.API.Application.Commands;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
@ -26,21 +27,30 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
||||
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
|
||||
}
|
||||
|
||||
[Route("new")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CreateOrder([FromBody]CreateOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId)
|
||||
[Route("cancel")]
|
||||
[HttpPut]
|
||||
public async Task<IActionResult> CancelOrder([FromBody]CancelOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId)
|
||||
{
|
||||
bool commandResult = false;
|
||||
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
|
||||
{
|
||||
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(command, guid);
|
||||
commandResult = await _mediator.Send(requestCreateOrder);
|
||||
var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(command, guid);
|
||||
commandResult = await _mediator.Send(requestCancelOrder);
|
||||
}
|
||||
else
|
||||
|
||||
return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest();
|
||||
|
||||
}
|
||||
|
||||
[Route("ship")]
|
||||
[HttpPut]
|
||||
public async Task<IActionResult> ShipOrder([FromBody]ShipOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId)
|
||||
{
|
||||
bool commandResult = false;
|
||||
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
|
||||
{
|
||||
// If no x-requestid header is found we process the order anyway. This is just temporary to not break existing clients
|
||||
// that aren't still updated. When all clients were updated this could be removed.
|
||||
commandResult = await _mediator.Send(command);
|
||||
var requestShipOrder = new IdentifiedCommand<ShipOrderCommand, bool>(command, guid);
|
||||
commandResult = await _mediator.Send(requestShipOrder);
|
||||
}
|
||||
|
||||
return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user