Browse Source

Merge pull request #2 from Espent1004/eventHandlingCustomisation

Event handling customisation
pull/1240/head
Espent1004 5 years ago
committed by GitHub
parent
commit
71e52e9f89
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 2221 additions and 170 deletions
  1. +1
    -1
      docker-compose.dcproj
  2. +11
    -1
      docker-compose.override.yml
  3. +8
    -0
      docker-compose.yml
  4. +54
    -54
      eShopOnContainers-ServicesAndWebApps.sln
  5. +74
    -0
      src/BuildingBlocks/EventBus/EventBus/Abstractions/AbstractIntegrationEventHandler.cs
  6. +6
    -0
      src/BuildingBlocks/EventBus/EventBus/EventBus.csproj
  7. +20
    -0
      src/BuildingBlocks/EventBus/EventBus/Events/CustomisationEvent.cs
  8. +18
    -0
      src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs
  9. +125
    -22
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
  10. +35
    -29
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs
  11. +183
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Controllers/SavedEventsController.cs
  12. +106
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Controllers/ShippingInformationsController.cs
  13. +58
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Controllers/ValuesController.cs
  14. +34
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Database/DbInitializer.cs
  15. +11
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Database/SavedEvent.cs
  16. +38
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Database/TenantAContext.cs
  17. +59
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Dockerfile
  18. +12
    -0
      src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/IRFIDService.cs
  19. +13
    -0
      src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/IShippingService.cs
  20. +23
    -0
      src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/MockedShippingService.cs
  21. +12
    -0
      src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/Models/Fragility.cs
  22. +12
    -0
      src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/Models/Priority.cs
  23. +17
    -0
      src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/Models/ShippingInformation.cs
  24. +33
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Infrastructure/AutofacModules/ApplicationModule.cs
  25. +12
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Infrastructure/AutofacModules/MediatorModule.cs
  26. +33
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs
  27. +69
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
  28. +49
    -0
      src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/EventHandling/CustomisationEventHandler.cs
  29. +45
    -0
      src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs
  30. +60
    -0
      src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/EventHandling/TenantAUserCheckoutAcceptedIntegrationEventHandler.cs
  31. +30
    -0
      src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs
  32. +22
    -0
      src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/Events/OrderStatusChangedToSubmittedIntegrationEvent.cs
  33. +62
    -0
      src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs
  34. +89
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Program.cs
  35. +30
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Properties/launchSettings.json
  36. +14
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Services/IValidationService.cs
  37. +16
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Services/ValidationService.cs
  38. +416
    -0
      src/Services/TenantCustomisations/TenantACustomisations/Startup.cs
  39. +58
    -0
      src/Services/TenantCustomisations/TenantACustomisations/TenantACustomisations.csproj
  40. +9
    -0
      src/Services/TenantCustomisations/TenantACustomisations/appsettings.Development.json
  41. +29
    -0
      src/Services/TenantCustomisations/TenantACustomisations/appsettings.json
  42. +14
    -0
      src/Services/TenantManager/TenantManager/Controllers/CustomisationsController.cs
  43. +11
    -32
      src/Services/TenantManager/TenantManager/Database/DbInitializer.cs
  44. +8
    -1
      src/Services/TenantManager/TenantManager/Models/Customisation.cs
  45. +1
    -1
      src/Services/TenantManager/TenantManager/Models/Methods.cs
  46. +2
    -2
      src/Services/TenantManager/TenantManager/Models/Tenant.cs
  47. +81
    -3
      src/Web/WebMVC/Controllers/OrderController.cs
  48. +12
    -0
      src/Web/WebMVC/ViewModels/Customisation/Fragility.cs
  49. +12
    -0
      src/Web/WebMVC/ViewModels/Customisation/Priority.cs
  50. +17
    -0
      src/Web/WebMVC/ViewModels/Customisation/ShippingInformation.cs
  51. +18
    -13
      src/Web/WebMVC/Views/Order/Detail.cshtml
  52. +39
    -11
      src/Web/WebMVC/Views/Order/Index.cshtml

+ 1
- 1
docker-compose.dcproj View File

@ -7,7 +7,7 @@
<DockerServiceName>webmvc</DockerServiceName>
<DockerTargetOS>Linux</DockerTargetOS>
<ProjectVersion>2.1</ProjectVersion>
<DockerLaunchAction>LaunchBrowser</DockerLaunchAction>
<DockerLaunchAction>None</DockerLaunchAction>
</PropertyGroup>
<ItemGroup>
<None Include=".dockerignore" />


+ 11
- 1
docker-compose.override.yml View File

@ -59,10 +59,20 @@ services:
tenantmanager:
environment:
- ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.TenantManagerDb;User Id=sa;Password=Pass@word}
ports:
- "5115:80"
tenantacustomisation:
environment:
- EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq}
- EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
- EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
- AzureServiceBusEnabled=False
- ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.TenantADb;User Id=sa;Password=Pass@word}
ports:
- "5116:80"
basket.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development


+ 8
- 0
docker-compose.yml View File

@ -43,6 +43,14 @@ services:
depends_on:
- sql.data
tenantacustomisation:
image: ${REGISTRY:-eshop}/tenantacustomisation:${PLATFORM:-linux}-${TAG:-latest}
build:
context: .
dockerfile: src/Services/TenantCustomisations/TenantACustomisations/Dockerfile
depends_on:
- sql.data
catalog.api:
image: ${REGISTRY:-eshop}/catalog.api:${PLATFORM:-linux}-${TAG:-latest}
build:


+ 54
- 54
eShopOnContainers-ServicesAndWebApps.sln View File

@ -150,14 +150,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Devspace.Support", "Devspac
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devspaces.Support", "src\BuildingBlocks\Devspaces.Support\Devspaces.Support.csproj", "{56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TenantCustomisations", "TenantCustomisations", "{D2D53ADC-5230-4EF4-95B0-31FD734992F6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TenantACustomisations", "..\TenantACustomisations\TenantACustomisations.csproj", "{6CBF0D7F-7566-428E-B704-4059A10FFD52}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TenantManager", "TenantManager", "{71587505-2945-4286-BF0B-BA4C004B0BA7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TenantManager", "src\Services\TenantManager\TenantManager\TenantManager.csproj", "{9C101827-119D-44EE-B0F0-94E7ABA8AE6A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TenantCustomisations", "TenantCustomisations", "{773A0C2A-CA6F-4D4A-860B-C518EFA6FACB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TenantACustomisations", "src\Services\TenantCustomisations\TenantACustomisations\TenantACustomisations.csproj", "{76651DAE-FF27-44A4-AF84-34689E2FFDF2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@ -1804,54 +1804,6 @@ Global
{56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x64.Build.0 = Release|Any CPU
{56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x86.ActiveCfg = Release|Any CPU
{56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x86.Build.0 = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|ARM.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|iPhone.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|x64.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|x64.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|x86.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.AppStore|x86.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|ARM.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|ARM.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|iPhone.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|x64.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|x64.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|x86.ActiveCfg = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Debug|x86.Build.0 = Debug|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|Any CPU.Build.0 = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|ARM.ActiveCfg = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|ARM.Build.0 = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|iPhone.ActiveCfg = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|iPhone.Build.0 = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|x64.ActiveCfg = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|x64.Build.0 = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|x86.ActiveCfg = Release|Any CPU
{6CBF0D7F-7566-428E-B704-4059A10FFD52}.Release|x86.Build.0 = Release|Any CPU
{9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -1900,6 +1852,54 @@ Global
{9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Release|x64.Build.0 = Release|Any CPU
{9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Release|x86.ActiveCfg = Release|Any CPU
{9C101827-119D-44EE-B0F0-94E7ABA8AE6A}.Release|x86.Build.0 = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|ARM.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|iPhone.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|x64.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|x64.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|x86.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.AppStore|x86.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|ARM.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|ARM.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|iPhone.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|x64.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|x64.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|x86.ActiveCfg = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Debug|x86.Build.0 = Debug|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|Any CPU.Build.0 = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|ARM.ActiveCfg = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|ARM.Build.0 = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|iPhone.ActiveCfg = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|iPhone.Build.0 = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|x64.ActiveCfg = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|x64.Build.0 = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|x86.ActiveCfg = Release|Any CPU
{76651DAE-FF27-44A4-AF84-34689E2FFDF2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1966,10 +1966,10 @@ Global
{766D7E92-6AF0-476C-ADD5-282BF4D8C576} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
{68F5041D-51F2-4630-94B6-B49789F5E51A} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35} = {68F5041D-51F2-4630-94B6-B49789F5E51A}
{D2D53ADC-5230-4EF4-95B0-31FD734992F6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{6CBF0D7F-7566-428E-B704-4059A10FFD52} = {D2D53ADC-5230-4EF4-95B0-31FD734992F6}
{71587505-2945-4286-BF0B-BA4C004B0BA7} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{9C101827-119D-44EE-B0F0-94E7ABA8AE6A} = {71587505-2945-4286-BF0B-BA4C004B0BA7}
{773A0C2A-CA6F-4D4A-860B-C518EFA6FACB} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{76651DAE-FF27-44A4-AF84-34689E2FFDF2} = {773A0C2A-CA6F-4D4A-860B-C518EFA6FACB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9}


+ 74
- 0
src/BuildingBlocks/EventBus/EventBus/Abstractions/AbstractIntegrationEventHandler.cs View File

@ -0,0 +1,74 @@
using System.Threading.Tasks;
using System;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System.Net;
using System.IO;
using System.Net.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.Extensions.Logging;
namespace Ordering.API.Application.IntegrationEvents.EventHandling
{
public abstract class AbstractIntegrationEventHandler<IIntegrationEvent>
{
private static String url = @"http://tenantmanager/";
private readonly IEventBus _eventBus;
//private readonly ILogger<AbstractIntegrationEventHandler<IIntegrationEvent>> _logger;
protected AbstractIntegrationEventHandler(IEventBus eventBus)
{
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
}
public async Task<bool> CheckIfCustomised(IntegrationEvent @event)
{
if (!@event.CheckForCustomisation)
{
return false;
}
Boolean result = Get(@event);
if (result)
{
CustomisationEvent customisationEvent = new CustomisationEvent(1, @event);
try
{
//_logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", eventMessage.Id, Program.AppName, eventMessage);
_eventBus.Publish(customisationEvent);
}
catch (Exception ex)
{
//_logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName}", eventMessage.Id, Program.AppName);
throw;
}
}
return result;
}
private Boolean Get(IntegrationEvent @event)
{
//TODO return true/false
Console.WriteLine("Making API Call...");
using (var client = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }))
{
client.BaseAddress = new Uri(url);
try
{
HttpResponseMessage response = client.GetAsync("api/tenants").Result;
response.EnsureSuccessStatusCode();
string result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine("Result: " + result);
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
return true;
}
}
}

+ 6
- 0
src/BuildingBlocks/EventBus/EventBus/EventBus.csproj View File

@ -9,4 +9,10 @@
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Logging.Abstractions">
<HintPath>..\..\..\..\..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.extensions.logging.abstractions\2.2.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

+ 20
- 0
src/BuildingBlocks/EventBus/EventBus/Events/CustomisationEvent.cs View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events
{
public class CustomisationEvent : IntegrationEvent
{
public CustomisationEvent(int tenantId, IntegrationEvent @event)
{
TenantId = tenantId;
this.@event = @event;
eventType = @event.GetType().Name;
}
public int TenantId { get; set; }
public IntegrationEvent @event { get; set; }
public String eventType { get; set; }
}
}

+ 18
- 0
src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs View File

@ -9,6 +9,16 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events
{
Id = Guid.NewGuid();
CreationDate = DateTime.UtcNow;
CheckForCustomisation = true;
TenantId = 1;
}
public IntegrationEvent(Boolean checkForCustomisation)
{
Id = Guid.NewGuid();
CreationDate = DateTime.UtcNow;
CheckForCustomisation = checkForCustomisation;
TenantId = 1;
}
[JsonConstructor]
@ -16,6 +26,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events
{
Id = id;
CreationDate = createDate;
TenantId = 1;
}
[JsonProperty]
@ -23,5 +34,12 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events
[JsonProperty]
public DateTime CreationDate { get; private set; }
[JsonProperty]
public Boolean CheckForCustomisation { get; set; }
//TODO fix this somehow
[JsonProperty]
public int TenantId { get; set; }
}
}

+ 125
- 22
src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs View File

@ -12,9 +12,15 @@ using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Net.Mime;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
{
@ -27,15 +33,20 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
private readonly IEventBusSubscriptionsManager _subsManager;
private readonly ILifetimeScope _autofac;
private readonly string AUTOFAC_SCOPE_NAME = "eshop_event_bus";
private static readonly String tenantACustomisationUrl = @"http://tenantacustomisation/";
private static readonly String tenantManagerUrl = @"http://tenantmanager/";
private readonly int _retryCount;
private IModel _consumerChannel;
private string _queueName;
public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger,
ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, string queueName = null, int retryCount = 5)
ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, string queueName = null,
int retryCount = 5)
{
_persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection));
_persistentConnection =
persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager();
_queueName = queueName;
@ -75,19 +86,22 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
var policy = RetryPolicy.Handle<BrokerUnreachableException>()
.Or<SocketException>()
.WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
{
_logger.LogWarning(ex, "Could not publish event: {EventId} after {Timeout}s ({ExceptionMessage})", @event.Id, $"{time.TotalSeconds:n1}", ex.Message);
});
.WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(ex, time) =>
{
_logger.LogWarning(ex,
"Could not publish event: {EventId} after {Timeout}s ({ExceptionMessage})", @event.Id,
$"{time.TotalSeconds:n1}", ex.Message);
});
var eventName = @event.GetType().Name;
_logger.LogTrace("Creating RabbitMQ channel to publish event: {EventId} ({EventName})", @event.Id, eventName);
_logger.LogWarning("Creating RabbitMQ channel to publish event: {EventId} ({EventName})", @event.Id,
eventName);
using (var channel = _persistentConnection.CreateModel())
{
_logger.LogTrace("Declaring RabbitMQ exchange to publish event: {EventId}", @event.Id);
_logger.LogWarning("Declaring RabbitMQ exchange to publish event: {EventId}", @event.Id);
channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct");
@ -99,7 +113,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
var properties = channel.CreateBasicProperties();
properties.DeliveryMode = 2; // persistent
_logger.LogTrace("Publishing event to RabbitMQ: {EventId}", @event.Id);
_logger.LogWarning("Publishing event to RabbitMQ: {EventId}", @event.Id);
channel.BasicPublish(
exchange: BROKER_NAME,
@ -114,7 +128,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
public void SubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
_logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName());
_logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName,
typeof(TH).GetGenericTypeName());
DoInternalSubscription(eventName);
_subsManager.AddDynamicSubscription<TH>(eventName);
@ -128,7 +143,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
var eventName = _subsManager.GetEventKey<T>();
DoInternalSubscription(eventName);
_logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName());
_logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName,
typeof(TH).GetGenericTypeName());
_subsManager.AddSubscription<T, TH>();
StartBasicConsume();
@ -147,8 +163,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
using (var channel = _persistentConnection.CreateModel())
{
channel.QueueBind(queue: _queueName,
exchange: BROKER_NAME,
routingKey: eventName);
exchange: BROKER_NAME,
routingKey: eventName);
}
}
}
@ -238,13 +254,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
var channel = _persistentConnection.CreateModel();
channel.ExchangeDeclare(exchange: BROKER_NAME,
type: "direct");
type: "direct");
channel.QueueDeclare(queue: _queueName,
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.CallbackException += (sender, ea) =>
{
@ -258,9 +274,69 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
return channel;
}
private async void SendEventToTenant(String content, String id, String eventName)
{
var temp = new SavedEvent();
temp.Content = content;
temp.SavedEventId = id;
temp.EventName = eventName;
string myJson = JsonConvert.SerializeObject(temp);
using (var client = new HttpClient())
{
try
{
//TODO replace URL with response from tenantmanager
var response = await client.PostAsync(
tenantACustomisationUrl + "api/SavedEvents",
new StringContent(myJson, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
_logger.LogInformation("----- Event sent to tenant{@id} -----", id);
}
catch (Exception e)
{
_logger.LogInformation("----- Exception{@e} -- Event{@id} -----", e, @id);
}
}
}
private async Task<Boolean> IsEventCustomised(String eventName, int tenantId)
{
CustomisationInfo customisationInfo = new CustomisationInfo();
customisationInfo.EventName = eventName;
customisationInfo.TenantId = tenantId;
Boolean isCustomised = false;
var builder = new UriBuilder(tenantManagerUrl + "api/Customisations/IsCustomised");
builder.Port = -1;
var query = HttpUtility.ParseQueryString(builder.Query);
query["eventName"] = eventName;
query["tenantId"] = tenantId.ToString();
builder.Query = query.ToString();
string url = builder.ToString();
using (var client = new HttpClient())
{
try
{
var response = await client.GetAsync(
url);
response.EnsureSuccessStatusCode();
isCustomised =
JsonConvert.DeserializeObject<Boolean>(response.Content.ReadAsStringAsync().Result);
}
catch (Exception e)
{
_logger.LogInformation("----- Exception{@e}", e);
}
}
return isCustomised;
}
private async Task ProcessEvent(string eventName, string message)
{
_logger.LogTrace("Processing RabbitMQ event: {EventName}", eventName);
_logger.LogWarning("Processing RabbitMQ event: {EventName}", eventName);
if (_subsManager.HasSubscriptionsForEvent(eventName))
{
@ -271,7 +347,9 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
{
if (subscription.IsDynamic)
{
var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler;
//TODO check if it is required here aswell
var handler =
scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler;
if (handler == null) continue;
dynamic eventData = JObject.Parse(message);
@ -281,13 +359,25 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
else
{
var handler = scope.ResolveOptional(subscription.HandlerType);
if (handler == null) continue;
var eventType = _subsManager.GetEventTypeByName(eventName);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
if (integrationEvent is IntegrationEvent evt && IsEventCustomised(eventName, evt.TenantId).Result) //TODO replace with tenantmanager
{
//Checking if event should be sent to tenant, or handled normally
if (evt.CheckForCustomisation)
{
SendEventToTenant(message, evt.Id.ToString(), eventName);
break;
}
}
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
await Task.Yield();
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
await (Task) concreteType.GetMethod("Handle")
.Invoke(handler, new object[] {integrationEvent});
}
}
}
@ -299,3 +389,16 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
}
}
}
class SavedEvent
{
public string SavedEventId { get; set; }
public string Content { get; set; }
public String EventName { get; set; }
}
class CustomisationInfo
{
public string EventName { get; set; }
public int TenantId { get; set; }
}

+ 35
- 29
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs View File

@ -15,14 +15,16 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling
public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>
{
private readonly IMediator _mediator;
private readonly IEventBus _eventBus;
private readonly ILogger<UserCheckoutAcceptedIntegrationEventHandler> _logger;
public UserCheckoutAcceptedIntegrationEventHandler(
IMediator mediator,
ILogger<UserCheckoutAcceptedIntegrationEventHandler> logger)
ILogger<UserCheckoutAcceptedIntegrationEventHandler> logger, IEventBus eventBus)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
}
/// <summary>
@ -40,42 +42,46 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
var result = false;
//var customised = await CheckIfCustomised(@event);
//if (!customised)
//{
var result = false;
if (@event.RequestId != Guid.Empty)
{
using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId))
if (@event.RequestId != Guid.Empty)
{
var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street,
@event.State, @event.Country, @event.ZipCode,
@event.CardNumber, @event.CardHolderName, @event.CardExpiration,
@event.CardSecurityNumber, @event.CardTypeId);
using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId))
{
var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street,
@event.State, @event.Country, @event.ZipCode,
@event.CardNumber, @event.CardHolderName, @event.CardExpiration,
@event.CardSecurityNumber, @event.CardTypeId);
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, @event.RequestId);
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, @event.RequestId);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
requestCreateOrder.GetGenericTypeName(),
nameof(requestCreateOrder.Id),
requestCreateOrder.Id,
requestCreateOrder);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
requestCreateOrder.GetGenericTypeName(),
nameof(requestCreateOrder.Id),
requestCreateOrder.Id,
requestCreateOrder);
result = await _mediator.Send(requestCreateOrder);
result = await _mediator.Send(requestCreateOrder);
if (result)
{
_logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId);
}
else
{
_logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId);
if (result)
{
_logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId);
}
else
{
_logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId);
}
}
}
}
else
{
_logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event);
}
else
{
_logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event);
}
//}
}
}
}

+ 183
- 0
src/Services/TenantCustomisations/TenantACustomisations/Controllers/SavedEventsController.cs View File

@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using TenantACustomisations.Database;
using TenantACustomisations.IntegrationEvents.Events;
namespace TenantACustomisations.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class SavedEventsController : ControllerBase
{
private readonly TenantAContext _context;
private readonly ILogger<SavedEventsController> _logger;
private readonly IEventBus _eventBus;
private List<Type> types = new List<Type>()
{
typeof(OrderStatusChangedToSubmittedIntegrationEvent),
typeof(OrderStatusChangedToAwaitingValidationIntegrationEvent)
};
public SavedEventsController(TenantAContext context, ILogger<SavedEventsController> logger, IEventBus eventBus)
{
_context = context;
_logger = logger;
_eventBus = eventBus;
}
// GET: api/SavedEvents
[HttpGet]
public async Task<ActionResult<IEnumerable<SavedEvent>>> GetSavedEvent(String orderId)
{
if (String.IsNullOrEmpty(orderId))
{
return await _context.SavedEvent.ToListAsync();
}
//Getting saved events
var savedEvents = await _context.SavedEvent.ToListAsync();
//Returning if list is empty
if (savedEvents.Count == 0)
{
return NotFound();
}
List<IntegrationEvent> events = new List<IntegrationEvent>();
//Converting events to actual type
savedEvents.ForEach(e =>
{
var integrationEvent =JsonConvert.DeserializeObject(e.Content, GetEventTypeByName(e.EventName));
IntegrationEvent evt = (IntegrationEvent)integrationEvent;
events.Add(evt);
});
bool found = false;
//Casting to class to check the orderId
events.ForEach(e =>
{
if(e is OrderStatusChangedToAwaitingValidationIntegrationEvent)
{
OrderStatusChangedToAwaitingValidationIntegrationEvent evt = (OrderStatusChangedToAwaitingValidationIntegrationEvent)e;
if (evt.OrderId == Int32.Parse(orderId))
{
found = true;
}
}
else if(e is OrderStatusChangedToSubmittedIntegrationEvent)
{
OrderStatusChangedToSubmittedIntegrationEvent evt = (OrderStatusChangedToSubmittedIntegrationEvent)e;
if (evt.OrderId == Int32.Parse(orderId))
{
found = true;
}
}
});
if (!found)
{
return NotFound();
}
return savedEvents;
}
// PUT: api/SavedEvents/5
[HttpPut("{id}")]
public async Task<IActionResult> PutSavedEvent(string id, SavedEvent savedEvent)
{
if (id != savedEvent.SavedEventId)
{
return BadRequest();
}
_context.Entry(savedEvent).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!SavedEventExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/SavedEvents
[HttpPost]
public async Task<ActionResult<SavedEvent>> PostSavedEvent(SavedEvent savedEvent)
{
_context.SavedEvent.Add(savedEvent);
await _context.SaveChangesAsync();
return CreatedAtAction("GetSavedEvent", new {id = savedEvent.SavedEventId}, savedEvent);
}
// DELETE: api/SavedEvents/5
[HttpDelete("{id}")]
public async Task<ActionResult<SavedEvent>> DeleteSavedEvent(string id)
{
var savedEvent = await _context.SavedEvent.FindAsync(id);
if (savedEvent == null)
{
return NotFound();
}
var integrationEvent =
JsonConvert.DeserializeObject(savedEvent.Content, GetEventTypeByName(savedEvent.EventName));
IntegrationEvent evt = (IntegrationEvent) integrationEvent;
try
{
_logger.LogInformation(
"----- Publishing integration event: {IntegrationEventId} from OrderStatusChangedToSubmittedIntegrationEventsController - ({@IntegrationEvent})",
evt.Id, evt);
evt.CheckForCustomisation = false;
_eventBus.Publish(evt);
_context.SavedEvent.Remove(savedEvent);
await _context.SaveChangesAsync();
return savedEvent;
}
catch (Exception ex)
{
_logger.LogError(ex,
"ERROR Publishing integration event: {IntegrationEventId} from OrderStatusChangedToSubmittedIntegrationEventsController",
evt.Id);
throw;
}
_context.SavedEvent.Remove(savedEvent);
await _context.SaveChangesAsync();
return savedEvent;
}
private bool SavedEventExists(string id)
{
return _context.SavedEvent.Any(e => e.SavedEventId == id);
}
private Type GetEventTypeByName(string eventName) => types.SingleOrDefault(t => t.Name == eventName);
}
}

+ 106
- 0
src/Services/TenantCustomisations/TenantACustomisations/Controllers/ShippingInformationsController.cs View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TenantACustomisations.Database;
using TenantACustomisations.ExternalServices;
namespace TenantACustomisations.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ShippingInformationsController : ControllerBase
{
private readonly TenantAContext _context;
public ShippingInformationsController(TenantAContext context)
{
_context = context;
}
// GET: api/ShippingInformations
[HttpGet]
public async Task<ActionResult<IEnumerable<ShippingInformation>>> GetShippingInformation()
{
return await _context.ShippingInformation.ToListAsync();
}
// GET: api/ShippingInformations/5
[HttpGet("{id}")]
public async Task<ActionResult<ShippingInformation>> GetShippingInformation(int id)
{
var shippingInformation = await _context.ShippingInformation.FindAsync(id);
if (shippingInformation == null)
{
return NotFound();
}
return shippingInformation;
}
// PUT: api/ShippingInformations/5
[HttpPut("{id}")]
public async Task<IActionResult> PutShippingInformation(int id, ShippingInformation shippingInformation)
{
if (id != shippingInformation.ShippingInformationId)
{
return BadRequest();
}
_context.Entry(shippingInformation).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ShippingInformationExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/ShippingInformations
[HttpPost]
public async Task<ActionResult<ShippingInformation>> PostShippingInformation(ShippingInformation shippingInformation)
{
_context.ShippingInformation.Add(shippingInformation);
await _context.SaveChangesAsync();
return CreatedAtAction("GetShippingInformation", new { id = shippingInformation.ShippingInformationId }, shippingInformation);
}
// DELETE: api/ShippingInformations/5
[HttpDelete("{id}")]
public async Task<ActionResult<ShippingInformation>> DeleteShippingInformation(int id)
{
var shippingInformation = await _context.ShippingInformation.FindAsync(id);
if (shippingInformation == null)
{
return NotFound();
}
_context.ShippingInformation.Remove(shippingInformation);
await _context.SaveChangesAsync();
return shippingInformation;
}
private bool ShippingInformationExists(int id)
{
return _context.ShippingInformation.Any(e => e.ShippingInformationId == id);
}
}
}

+ 58
- 0
src/Services/TenantCustomisations/TenantACustomisations/Controllers/ValuesController.cs View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TenantACustomisations.Database;
using TenantACustomisations.ExternalServices;
namespace TenantACustomisations.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly TenantAContext _context;
public ValuesController(TenantAContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
// GET api/values
[HttpGet]
public async Task<ActionResult<IEnumerable<ShippingInformation>>> GetShippingInformation()
{
return await _context.ShippingInformation.ToListAsync();
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody] string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}

+ 34
- 0
src/Services/TenantCustomisations/TenantACustomisations/Database/DbInitializer.cs View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TenantACustomisations.ExternalServices;
namespace TenantACustomisations.Database
{
public class DbInitializer
{
public void Initialize(TenantAContext context)
{
context.Database.EnsureCreated();
if (context.ShippingInformation.Any())
{
return;
}
ShippingInformation shippingInformation = new ShippingInformation();
shippingInformation.ShippingTime = DateTime.Today;
shippingInformation.ArrivalTime = DateTime.Today.AddDays(2);
shippingInformation.FragilityLevel = Fragility.Medium;
shippingInformation.PriorityLevel = Priority.High;
shippingInformation.ShippingInformationId = 1;
shippingInformation.OrderNumber = "1";
context.ShippingInformation.Add(shippingInformation);
context.SaveChanges();
}
}
}

+ 11
- 0
src/Services/TenantCustomisations/TenantACustomisations/Database/SavedEvent.cs View File

@ -0,0 +1,11 @@
using System;
namespace TenantACustomisations.Database
{
public class SavedEvent
{
public string SavedEventId { get; set; }
public string Content { get; set; }
public String EventName { get; set; }
}
}

+ 38
- 0
src/Services/TenantCustomisations/TenantACustomisations/Database/TenantAContext.cs View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using TenantACustomisations.ExternalServices;
using TenantACustomisations.IntegrationEvents.Events;
namespace TenantACustomisations.Database
{
public class TenantAContext : DbContext
{
public TenantAContext(DbContextOptions<TenantAContext> options)
: base(options)
{
}
public DbSet<ShippingInformation> ShippingInformation { get; set; }
public DbSet<SavedEvent> SavedEvent
{
get;
set;
}
}
public class TenantAContextDesignFactory : IDesignTimeDbContextFactory<TenantAContext>
{
public TenantAContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<TenantAContext>()
.UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.TenantADb;Integrated Security=true");
return new TenantAContext(optionsBuilder.Options);
}
}
}

+ 59
- 0
src/Services/TenantCustomisations/TenantACustomisations/Dockerfile View File

@ -0,0 +1,59 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
WORKDIR /src
# Keep the project list and command dotnet restore identical in all Dockfiles to maximize image cache utilization
COPY eShopOnContainers-ServicesAndWebApps.sln .
COPY docker-compose.dcproj /src/
COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/
COPY src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj src/ApiGateways/Mobile.Bff.Shopping/aggregator/
COPY src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj src/ApiGateways/Web.Bff.Shopping/aggregator/
COPY src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj src/BuildingBlocks/Devspaces.Support/
COPY src/BuildingBlocks/EventBus/EventBus/EventBus.csproj src/BuildingBlocks/EventBus/EventBus/
COPY src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj src/BuildingBlocks/EventBus/EventBus.Tests/
COPY src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj src/BuildingBlocks/EventBus/EventBusRabbitMQ/
COPY src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj src/BuildingBlocks/EventBus/EventBusServiceBus/
COPY src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj src/BuildingBlocks/EventBus/IntegrationEventLogEF/
COPY src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj src/BuildingBlocks/WebHostCustomization/WebHost.Customization/
COPY src/Services/Basket/Basket.API/Basket.API.csproj src/Services/Basket/Basket.API/
COPY src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj src/Services/Basket/Basket.FunctionalTests/
COPY src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj src/Services/Basket/Basket.UnitTests/
COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj src/Services/Catalog/Catalog.API/
COPY src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj src/Services/Catalog/Catalog.FunctionalTests/
COPY src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj src/Services/Catalog/Catalog.UnitTests/
COPY src/Services/Identity/Identity.API/Identity.API.csproj src/Services/Identity/Identity.API/
COPY src/Services/Location/Locations.API/Locations.API.csproj src/Services/Location/Locations.API/
COPY src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj src/Services/Location/Locations.FunctionalTests/
COPY src/Services/Marketing/Marketing.API/Marketing.API.csproj src/Services/Marketing/Marketing.API/
COPY src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj src/Services/Marketing/Marketing.FunctionalTests/
COPY src/Services/Ordering/Ordering.API/Ordering.API.csproj src/Services/Ordering/Ordering.API/
COPY src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj src/Services/Ordering/Ordering.BackgroundTasks/
COPY src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj src/Services/Ordering/Ordering.Domain/
COPY src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj src/Services/Ordering/Ordering.FunctionalTests/
COPY src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj src/Services/Ordering/Ordering.Infrastructure/
COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/
COPY src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj src/Services/Ordering/Ordering.UnitTests/
COPY src/Services/Payment/Payment.API/Payment.API.csproj src/Services/Payment/Payment.API/
COPY src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj src/Services/Webhooks/Webhooks.API/
COPY src/Web/WebhookClient/WebhookClient.csproj src/Web/WebhookClient/
COPY src/Web/WebMVC/WebMVC.csproj src/Web/WebMVC/
COPY src/Web/WebSPA/WebSPA.csproj src/Web/WebSPA/
COPY src/Web/WebStatus/WebStatus.csproj src/Web/WebStatus/
COPY test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj test/ServicesTests/Application.FunctionalTests/
COPY test/ServicesTests/LoadTest/LoadTest.csproj test/ServicesTests/LoadTest/
RUN dotnet restore eShopOnContainers-ServicesAndWebApps.sln
COPY . .
WORKDIR /src/src/Services/TenantCustomisations/TenantACustomisations
RUN dotnet publish --no-restore -c Release -o /app
FROM build AS publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "TenantACustomisations.dll"]

+ 12
- 0
src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/IRFIDService.cs View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.ExternalServices
{
public interface IRFIDService
{
bool IsOrderRFIDTagged(int orderNumber);
}
}

+ 13
- 0
src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/IShippingService.cs View File

@ -0,0 +1,13 @@
using Ordering.API.Application.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.ExternalServices
{
public interface IShippingService
{
ShippingInformation CalculateShippingInformation(int orderId);
}
}

+ 23
- 0
src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/MockedShippingService.cs View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ordering.API.Application.Models;
namespace TenantACustomisations.ExternalServices
{
public class MockedShippingService : IShippingService
{
public ShippingInformation CalculateShippingInformation(int orderId)
{
ShippingInformation shippingInformation = new ShippingInformation();
shippingInformation.ShippingTime = DateTime.Today;
shippingInformation.ArrivalTime = DateTime.Today.AddDays(2);
shippingInformation.FragilityLevel = Fragility.Medium;
shippingInformation.PriorityLevel = Priority.High;
shippingInformation.OrderNumber = orderId.ToString();
return shippingInformation;
}
}
}

+ 12
- 0
src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/Models/Fragility.cs View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.ExternalServices
{
public enum Fragility
{
Low, Medium, High
}
}

+ 12
- 0
src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/Models/Priority.cs View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.ExternalServices
{
public enum Priority
{
Low, Medium, High
}
}

+ 17
- 0
src/Services/TenantCustomisations/TenantACustomisations/ExternalServices/Models/ShippingInformation.cs View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.ExternalServices
{
public class ShippingInformation
{
public int ShippingInformationId { get; set; }
public DateTime ArrivalTime { get; set; }
public DateTime ShippingTime { get; set; }
public Priority PriorityLevel {get;set;}
public Fragility FragilityLevel { get; set; }
public String OrderNumber { get; set; }
}
}

+ 33
- 0
src/Services/TenantCustomisations/TenantACustomisations/Infrastructure/AutofacModules/ApplicationModule.cs View File

@ -0,0 +1,33 @@
using Autofac;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using System.Reflection;
using TenantACustomisations.ExternalServices;
using TenantACustomisations.IntegrationEvents.Events;
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations.Infrastructure.AutofacModules
{
public class ApplicationModule
:Autofac.Module
{
public string QueriesConnectionString { get; }
public ApplicationModule(string qconstr)
{
QueriesConnectionString = qconstr;
}
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(UserCheckoutAcceptedIntegrationEvent).GetTypeInfo().Assembly)
.AsClosedTypesOf(typeof(IIntegrationEventHandler<>));
builder.RegisterType<MockedShippingService>()
.As<IShippingService>()
.InstancePerLifetimeScope();
}
}
}

+ 12
- 0
src/Services/TenantCustomisations/TenantACustomisations/Infrastructure/AutofacModules/MediatorModule.cs View File

@ -0,0 +1,12 @@
using Autofac;
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations.Infrastructure.AutofacModules
{
public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
//TODO
}
}
}

+ 33
- 0
src/Services/TenantCustomisations/TenantACustomisations/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs View File

@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.Infrastructure.Filters
{
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
// Check for authorize attribute
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() ||
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
if (!hasAuthorize) return;
operation.Responses.TryAdd("401", new Response { Description = "Unauthorized" });
operation.Responses.TryAdd("403", new Response { Description = "Forbidden" });
operation.Security = new List<IDictionary<string, IEnumerable<string>>>
{
new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", new [] { "orderingapi" } }
}
};
}
}
}

+ 69
- 0
src/Services/TenantCustomisations/TenantACustomisations/Infrastructure/Filters/HttpGlobalExceptionFilter.cs View File

@ -0,0 +1,69 @@
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations.Infrastructure.Filters
{
using AspNetCore.Mvc;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System.Net;
public class HttpGlobalExceptionFilter : IExceptionFilter
{
private readonly IHostingEnvironment env;
private readonly ILogger<HttpGlobalExceptionFilter> logger;
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger)
{
this.env = env;
this.logger = logger;
}
public void OnException(ExceptionContext context)
{
logger.LogError(new EventId(context.Exception.HResult),
context.Exception,
context.Exception.Message);
if (1==2)//TODO
{
var problemDetails = new ValidationProblemDetails()
{
Instance = context.HttpContext.Request.Path,
Status = StatusCodes.Status400BadRequest,
Detail = "Please refer to the errors property for additional details."
};
problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() });
context.Result = new BadRequestObjectResult(problemDetails);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
else
{
var json = new JsonErrorResponse
{
Messages = new[] { "An error occur.Try it again." }
};
if (env.IsDevelopment())
{
json.DeveloperMessage = context.Exception;
}
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1
// It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information
//TODO
//context.Result = new InternalServerErrorObjectResult(json);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
context.ExceptionHandled = true;
}
private class JsonErrorResponse
{
public string[] Messages { get; set; }
public object DeveloperMessage { get; set; }
}
}
}

+ 49
- 0
src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/EventHandling/CustomisationEventHandler.cs View File

@ -0,0 +1,49 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ordering.API.Application.IntegrationEvents.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TenantACustomisations.Services;
namespace TenantACustomisations.IntegrationEvents.EventHandling
{
public class CustomisationEventHandler : IIntegrationEventHandler<CustomisationEvent>
{
private readonly ILogger<CustomisationEventHandler> _logger;
private readonly IEventBus _eventBus;
private readonly IValidationService validationService;
public CustomisationEventHandler(ILogger<CustomisationEventHandler> logger, IEventBus eventBus)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
validationService = new ValidationService();
}
public async Task Handle(CustomisationEvent @event)
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} - ({@IntegrationEvent})", @event.Id, @event);
IntegrationEvent integrationEvent = @event.@event;
switch (integrationEvent.GetType().Name)
{
case "UserCheckoutAcceptedIntegrationEvent":
if (validationService.Validate((UserCheckoutAcceptedIntegrationEvent)integrationEvent))
{
integrationEvent.CheckForCustomisation = false;
_eventBus.Publish(integrationEvent);
}
break;
default:
integrationEvent.CheckForCustomisation = false;
_eventBus.Publish(integrationEvent);
break;
}
}
}
}

+ 45
- 0
src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs View File

@ -0,0 +1,45 @@
using Microsoft.AspNetCore.SignalR;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using TenantACustomisations.Database;
using TenantACustomisations.ExternalServices;
using TenantACustomisations.IntegrationEvents.Events;
namespace TenantACustomisations.IntegrationEvents.EventHandling
{
public class OrderStatusChangedToSubmittedIntegrationEventHandler :
IIntegrationEventHandler<OrderStatusChangedToSubmittedIntegrationEvent>
{
private readonly ILogger<OrderStatusChangedToSubmittedIntegrationEventHandler> _logger;
private readonly IShippingService _shippingService;
private readonly TenantAContext _context;
public OrderStatusChangedToSubmittedIntegrationEventHandler(ILogger<OrderStatusChangedToSubmittedIntegrationEventHandler> logger, IShippingService shippingService, TenantAContext context)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_shippingService = shippingService ?? throw new ArgumentNullException(nameof(shippingService));
_context = context ?? throw new ArgumentNullException(nameof(shippingService));
}
public async Task Handle(OrderStatusChangedToSubmittedIntegrationEvent @event)
{
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}- TenantA"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at TenantA - ({@IntegrationEvent})", @event.Id, @event);
_logger.LogInformation("Hello");
//TODO
Debug.WriteLine(@event);
ShippingInformation shippingInformation = _shippingService.CalculateShippingInformation(@event.OrderId);
_context.ShippingInformation.Add(shippingInformation);
_logger.LogInformation("----- Saving shipping information: {IntegrationEventId} at TenantA - ({@IntegrationEvent}) - {@ShippingInformation}", @event.Id, @event, shippingInformation);
_context.SaveChanges();
}
}
}
}

+ 60
- 0
src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/EventHandling/TenantAUserCheckoutAcceptedIntegrationEventHandler.cs View File

@ -0,0 +1,60 @@
using MediatR;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.Extensions.Logging;
using Ordering.API.Application.Behaviors;
using Serilog.Context;
using System.Threading.Tasks;
using System;
using System.Diagnostics;
using TenantACustomisations.IntegrationEvents.Events;
using TenantACustomisations.ExternalServices;
using TenantACustomisations.Database;
namespace TenantACustomisations.IntegrationEvents.EventHandling
{
public class TenantAUserCheckoutAcceptedIntegrationEventHandler :
IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>
{
private readonly IMediator _mediator;
private readonly IEventBus _eventBus;
private readonly ILogger<TenantAUserCheckoutAcceptedIntegrationEventHandler> _logger;
//private readonly TenantAContext _context;
//private readonly IShippingService _shippingService;
public TenantAUserCheckoutAcceptedIntegrationEventHandler(
IMediator mediator,
ILogger<TenantAUserCheckoutAcceptedIntegrationEventHandler> logger,
IEventBus eventBus
)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
}
/// <summary>
/// Integration event handler which starts the create order process
/// </summary>
/// <param name="@event">
/// 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 @event)
{
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}- TenantA"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at TenantA- ({@IntegrationEvent})", @event.Id, @event);
_logger.LogInformation("Hello");
//TODO
Debug.WriteLine(@event);
//Save shipping info
//Hard code view comp
//Retrieve shipping info and show
}
}
}
}

+ 30
- 0
src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs View File

@ -0,0 +1,30 @@
using System.Collections.Generic;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
namespace TenantACustomisations.IntegrationEvents.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;
}
}
}

+ 22
- 0
src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/Events/OrderStatusChangedToSubmittedIntegrationEvent.cs View File

@ -0,0 +1,22 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.IntegrationEvents.Events
{
public class OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent
{
public int OrderId { get; set; }
public string OrderStatus { get; set; }
public string BuyerName { get; set; }
public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName)
{
OrderId = orderId;
OrderStatus = orderStatus;
BuyerName = buyerName;
}
}
}

+ 62
- 0
src/Services/TenantCustomisations/TenantACustomisations/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs View File

@ -0,0 +1,62 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Ordering.API.Application.Models;
using System;
namespace TenantACustomisations.IntegrationEvents.Events
{
public class UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
{
public string UserId { get; }
public string UserName { 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 userName, 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;
UserName = userName;
}
}
}

+ 89
- 0
src/Services/TenantCustomisations/TenantACustomisations/Program.cs View File

@ -0,0 +1,89 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.eShopOnContainers.Services.TenantACustomisations;
using Microsoft.Extensions.Configuration;
using Serilog;
using System;
using System.IO;
using TenantACustomisations.Database;
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations
{
public class Program
{
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
public static int Main(string[] args)
{
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration);
try
{
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(false)
.UseStartup<Startup>()
.UseApplicationInsights()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseConfiguration(configuration)
.UseSerilog()
.Build();
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
var logstashUrl = configuration["Serilog:LogstashgUrl"];
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
var config = builder.Build();
if (config.GetValue<bool>("UseVault", false))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
}
}

+ 30
- 0
src/Services/TenantCustomisations/TenantACustomisations/Properties/launchSettings.json View File

@ -0,0 +1,30 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:57040",
"sslPort": 44328
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"TenantACustomisations": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

+ 14
- 0
src/Services/TenantCustomisations/TenantACustomisations/Services/IValidationService.cs View File

@ -0,0 +1,14 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Ordering.API.Application.IntegrationEvents.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.Services
{
interface IValidationService
{
Boolean Validate(UserCheckoutAcceptedIntegrationEvent @event);
}
}

+ 16
- 0
src/Services/TenantCustomisations/TenantACustomisations/Services/ValidationService.cs View File

@ -0,0 +1,16 @@
using Ordering.API.Application.IntegrationEvents.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TenantACustomisations.Services
{
public class ValidationService : IValidationService
{
public bool Validate(UserCheckoutAcceptedIntegrationEvent @event)
{
return @event.State == "Oslo";
}
}
}

+ 416
- 0
src/Services/TenantCustomisations/TenantACustomisations/Startup.cs View File

@ -0,0 +1,416 @@
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations
{
using AspNetCore.Http;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.ServiceFabric;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.ServiceBus;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using RabbitMQ.Client;
using Swashbuckle.AspNetCore.Swagger;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Infrastructure.AutofacModules;
using Microsoft.eShopOnContainers.Services.TenantACustomisations.Infrastructure.Filters;
using global::TenantACustomisations.Infrastructure.Filters;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using global::TenantACustomisations.IntegrationEvents.EventHandling;
using global::TenantACustomisations.IntegrationEvents.Events;
using global::TenantACustomisations.ExternalServices;
using global::TenantACustomisations.Database;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsights(Configuration)
.AddCustomMvc()
.AddHealthChecks(Configuration)
.AddCustomDbContext(Configuration)
.AddCustomSwagger(Configuration)
.AddCustomIntegrations(Configuration)
.AddCustomConfiguration(Configuration)
.AddEventBus(Configuration)
.AddCustomAuthentication(Configuration);
//configure autofac
var container = new ContainerBuilder();
container.Populate(services);
container.RegisterModule(new MediatorModule());
container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"]));
return new AutofacServiceProvider(container.Build());
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
//loggerFactory.AddAzureWebAppDiagnostics();
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}
app.UseCors("CorsPolicy");
app.UseHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
app.UseHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
ConfigureAuth(app);
app.UseMvcWithDefaultRoute();
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1");
c.OAuthClientId("orderingswaggerui");
c.OAuthAppName("Ordering Swagger UI");
});
ConfigureEventBus(app);
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService<TenantAContext>();
context.Database.EnsureCreated();
}
}
private void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>();
//eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>();
eventBus.Subscribe<OrderStatusChangedToSubmittedIntegrationEvent, OrderStatusChangedToSubmittedIntegrationEventHandler>();
}
protected virtual void ConfigureAuth(IApplicationBuilder app)
{
if (Configuration.GetValue<bool>("UseLoadTest"))
{
//app.UseMiddleware<ByPassAuthMiddleware>();
//TODO
}
app.UseAuthentication();
}
}
static class CustomExtensionsMethods
{
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration)
{
services.AddApplicationInsightsTelemetry(configuration);
var orchestratorType = configuration.GetValue<string>("OrchestratorType");
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
services.AddApplicationInsightsKubernetesEnricher();
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
return services;
}
public static IServiceCollection AddCustomMvc(this IServiceCollection services)
{
// Add framework services.
services.AddMvc(options =>
{
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddControllersAsServices(); //Injecting Controllers themselves thru DI
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
return services;
}
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
var hcBuilder = services.AddHealthChecks();
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy());
hcBuilder
.AddSqlServer(
configuration["ConnectionString"],
name: "OrderingDB-check",
tags: new string[] { "orderingdb" });
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
hcBuilder
.AddAzureServiceBusTopic(
configuration["EventBusConnection"],
topicName: "eshop_event_bus",
name: "ordering-servicebus-check",
tags: new string[] { "servicebus" });
}
else
{
hcBuilder
.AddRabbitMQ(
$"amqp://{configuration["EventBusConnection"]}",
name: "ordering-rabbitmqbus-check",
tags: new string[] { "rabbitmqbus" });
}
return services;
}
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<TenantAContext>(options =>
options.UseSqlServer(configuration["ConnectionString"]));
services.AddDbContext<IntegrationEventLogContext>(options =>
{
options.UseSqlServer(configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
});
return services;
}
public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration)
{
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Ordering HTTP API",
Version = "v1",
Description = "The Ordering Service HTTP API",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>()
{
{ "orders", "Ordering API" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
return services;
}
public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//services.AddTransient<IIdentityService, IdentityService>();
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c));
//services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>();
var serviceBusConnectionString = configuration["EventBusConnection"];
var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString);
return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger);
});
}
else
{
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
if (!string.IsNullOrEmpty(configuration["EventBusUserName"]))
{
factory.UserName = configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(configuration["EventBusPassword"]))
{
factory.Password = configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
}
return services;
}
public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
//services.Configure<OrderingSettings>(configuration);
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new ValidationProblemDetails(context.ModelState)
{
Instance = context.HttpContext.Request.Path,
Status = StatusCodes.Status400BadRequest,
Detail = "Please refer to the errors property for additional details."
};
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json", "application/problem+xml" }
};
};
});
return services;
}
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
var subscriptionClientName = configuration["SubscriptionClientName"];
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
});
}
else
{
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
});
}
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
//services.AddTransient<TenantAUserCheckoutAcceptedIntegrationEventHandler>();
return services;
}
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{
// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
var identityUrl = configuration.GetValue<string>("IdentityUrl");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "orders";
});
return services;
}
}
}

+ 58
- 0
src/Services/TenantCustomisations/TenantACustomisations/TenantACustomisations.csproj View File

@ -0,0 +1,58 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\eShopOnContainersCustomised</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.4" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Dapper" Version="1.50.7" />
<PackageReference Include="FluentValidation.AspNetCore" Version="7.5.0" />
<PackageReference Include="MediatR" Version="5.1.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="5.1.0" />
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.11.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.4" />
<PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="Serilog" Version="2.9.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Http" Version="4.2.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="4.0.1" />
<PackageReference Include="System.Reflection" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
<ProjectReference Include="..\..\Ordering\Ordering.API\Ordering.API.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.0" />
</ItemGroup>
</Project>

+ 9
- 0
src/Services/TenantCustomisations/TenantACustomisations/appsettings.Development.json View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

+ 29
- 0
src/Services/TenantCustomisations/TenantACustomisations/appsettings.json View File

@ -0,0 +1,29 @@
{
"Serilog": {
"SeqServerUrl": null,
"LogstashgUrl": null,
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.eShopOnContainers": "Information",
"System": "Warning"
}
}
},
"IdentityUrl": "http://localhost:5105",
//"ConnectionString": "127.0.0.1",
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.TenantADb;User Id=sa;Password=Pass@word;",
"AzureServiceBusEnabled": false,
"SubscriptionClientName": "TenantACustomisation",
"ApplicationInsights": {
"InstrumentationKey": ""
},
"EventBusRetryCount": 5,
"UseVault": false,
"Vault": {
"Name": "eshop",
"ClientId": "your-clien-id",
"ClientSecret": "your-client-secret"
}
}

+ 14
- 0
src/Services/TenantManager/TenantManager/Controllers/CustomisationsController.cs View File

@ -41,6 +41,20 @@ namespace TenantManager.Controllers
return customisation;
}
// GET: api/Customisations/5
[HttpGet("isCustomised")]
public async Task<ActionResult<Boolean>> IsCustomised(String eventName, int tenantId)
{
var customisation = await _context.Customisation.Include(c => c.Method).Include(c => c.Tenant).Where(c => c.Method.MethodName.Equals(eventName) && c.TenantId == tenantId).FirstOrDefaultAsync();
if (customisation == null)
{
return false;
}
return true;
}
// PUT: api/Customisations/5
[HttpPut("{id}")]


+ 11
- 32
src/Services/TenantManager/TenantManager/Database/DbInitializer.cs View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Linq;
using TenantManager.Models;
namespace TenantManager.Database
@ -17,28 +14,13 @@ namespace TenantManager.Database
return;
}
var tenant1 = new Tenant() { TenantName = "Tekna" };
var tenant2 = new Tenant() { TenantName = "NITO" };
var tenant3 = new Tenant() { TenantName = "LO" };
var tenant1 = new Tenant { TenantName = "Tekna" };
context.Tenant.Add(tenant1);
var method1 = new Method { MethodName = "OrderStatusChangedToSubmittedIntegrationEvent" };
var method2 = new Method { MethodName = "OrderStatusChangedToAwaitingValidationIntegrationEvent" };
var tenants = new Tenant[]
{
tenant1,
tenant2,
tenant3
};
foreach(Tenant t in tenants)
{
context.Tenant.Add(t);
}
context.SaveChanges();
var method1 = new Method() { MethodName = "GetPrice" };
var method2 = new Method() { MethodName = "GetItem" };
var methods = new Method[]
var methods = new[]
{
method1,
method2
@ -48,14 +30,11 @@ namespace TenantManager.Database
{
context.Method.Add(m);
}
context.SaveChanges();
var customisations = new Customisation[]
var customisations = new[]
{
new Customisation(){Tenant=tenant1, Method=method1 },
new Customisation(){Tenant=tenant1, Method=method2},
new Customisation(){Tenant=tenant2, Method=method1 }
new Customisation {Tenant=tenant1, Method=method1 },
new Customisation {Tenant=tenant1, Method=method2}
};
foreach(Customisation c in customisations)


+ 8
- 1
src/Services/TenantManager/TenantManager/Models/Customisation.cs View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
@ -8,9 +9,15 @@ namespace TenantManager.Models
public class Customisation
{
public int CustomisationId { get; set; }
//Foreign keys
public int TenantId { get; set; }
public virtual Tenant Tenant { get; set; }
public int MethodId { get; set; }
[ForeignKey("TenantId")]
public virtual Tenant Tenant { get; set; }
[ForeignKey("MethodId")]
public virtual Method Method { get; set; }
}
}

+ 1
- 1
src/Services/TenantManager/TenantManager/Models/Methods.cs View File

@ -10,6 +10,6 @@ namespace TenantManager.Models
{
public int MethodId { get; set; }
public String MethodName { get; set; }
public ICollection<Customisation> Customisations { get; set; }
public List<Customisation> Customisations { get; set; }
}
}

+ 2
- 2
src/Services/TenantManager/TenantManager/Models/Tenant.cs View File

@ -10,7 +10,7 @@ namespace TenantManager.Models
{
public String TenantName { get; set; }
[Key]
public long TenantId { get; set; }
public ICollection<Customisation> Customisations { get; set; }
public int TenantId { get; set; }
public List<Customisation> Customisations { get; set; }
}
}

+ 81
- 3
src/Web/WebMVC/Controllers/OrderController.cs View File

@ -2,8 +2,17 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.eShopOnContainers.WebMVC.ViewModels.Customisation;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Polly.CircuitBreaker;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{
@ -13,16 +22,21 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
private IOrderingService _orderSvc;
private IBasketService _basketSvc;
private readonly IIdentityParser<ApplicationUser> _appUserParser;
public OrderController(IOrderingService orderSvc, IBasketService basketSvc, IIdentityParser<ApplicationUser> appUserParser)
private static String tenantACustomisationsUrl = @"http://tenantacustomisation/";
private readonly ILogger<OrderController> _logger;
public OrderController(IOrderingService orderSvc, IBasketService basketSvc,
IIdentityParser<ApplicationUser> appUserParser, ILogger<OrderController> logger)
{
_appUserParser = appUserParser;
_orderSvc = orderSvc;
_basketSvc = basketSvc;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<IActionResult> Create()
{
var user = _appUserParser.Parse(HttpContext.User);
var order = await _basketSvc.GetOrderDraft(user.Id);
var vm = _orderSvc.MapUserInfoIntoOrder(user, order);
@ -49,7 +63,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
}
catch (BrokenCircuitException)
{
ModelState.AddModelError("Error", "It was not possible to create a new order, please try later on. (Business Msg Due to Circuit-Breaker)");
ModelState.AddModelError("Error",
"It was not possible to create a new order, please try later on. (Business Msg Due to Circuit-Breaker)");
}
return View("Create", model);
@ -66,6 +81,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
public async Task<IActionResult> Detail(string orderId)
{
var user = _appUserParser.Parse(HttpContext.User);
Boolean RFIDScanned = await AllGoodsRFIDScanned(orderId);
ViewData["RFIDScanned"] = RFIDScanned;
var order = await _orderSvc.GetOrder(user, orderId);
return View(order);
@ -75,7 +92,68 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{
var user = _appUserParser.Parse(HttpContext.User);
var vm = await _orderSvc.GetMyOrders(user);
List<ShippingInformation> shippingInformation = GetShippingInfo(vm);
_logger.LogInformation("----- Shipping info{@ShippingInformation}", shippingInformation);
ViewData["ShippingInfo"] = shippingInformation;
return View(vm);
}
private async Task<Boolean> AllGoodsRFIDScanned(String orderId)
{
var builder = new UriBuilder(tenantACustomisationsUrl + "api/SavedEvents");
builder.Port = -1;
var query = HttpUtility.ParseQueryString(builder.Query);
query["orderId"] = orderId;
builder.Query = query.ToString();
string url = builder.ToString();
using (var client = new HttpClient())
{
var response = await client.GetAsync(
url);
if (response.StatusCode.Equals(HttpStatusCode.NotFound))
{
return true;
}
return false;
}
}
private List<ShippingInformation> GetShippingInfo(List<Order> orders)
{
List<ShippingInformation> shippingInformation = new List<ShippingInformation>();
using (var client = new HttpClient(new HttpClientHandler
{AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate}))
{
client.BaseAddress = new Uri(tenantACustomisationsUrl);
try
{
HttpResponseMessage response = client.GetAsync("api/shippinginformations").Result;
response.EnsureSuccessStatusCode();
string result = response.Content.ReadAsStringAsync().Result;
_logger.LogInformation("----- Result{@result} -----", result);
List<ShippingInformation>
results = JsonConvert.DeserializeObject<List<ShippingInformation>>(result);
results.ForEach(s =>
{
if (orders.Any(item => item.OrderNumber.Equals(s.OrderNumber)))
{
shippingInformation.Add(s);
}
});
}
catch (Exception e)
{
Console.WriteLine(e);
_logger.LogInformation("----- Exception{@e} -----", e);
}
}
return shippingInformation;
}
}
}

+ 12
- 0
src/Web/WebMVC/ViewModels/Customisation/Fragility.cs View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Customisation
{
public enum Fragility
{
Low, Medium, High
}
}

+ 12
- 0
src/Web/WebMVC/ViewModels/Customisation/Priority.cs View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Customisation
{
public enum Priority
{
Low, Medium, High
}
}

+ 17
- 0
src/Web/WebMVC/ViewModels/Customisation/ShippingInformation.cs View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Customisation
{
public class ShippingInformation
{
public int ShippingInformationId { get; set; }
public DateTime ArrivalTime { get; set; }
public DateTime ShippingTime { get; set; }
public Priority PriorityLevel { get; set; }
public Fragility FragilityLevel { get; set; }
public String OrderNumber { get; set; }
}
}

+ 18
- 13
src/Web/WebMVC/Views/Order/Detail.cshtml View File

@ -3,9 +3,12 @@
@model Microsoft.eShopOnContainers.WebMVC.ViewModels.Order
@{
ViewData["Title"] = "Order Detail";
var headerList= new List<Header>() {
new Header() { Controller = "Catalog", Text = "Back to catalog" } };
ViewData["Title"] = "Order Detail";
var headerList = new List<Header>()
{
new Header() {Controller = "Catalog", Text = "Back to catalog"}
};
var rfidScanned = ViewData["RFIDScanned"];
}
<div class="esh-orders_detail">
@ -14,20 +17,22 @@
<div class="container">
<section class="esh-orders_detail-section">
<article class="esh-orders_detail-titles row">
<section class="esh-orders_detail-title col-3">Order number</section>
<section class="esh-orders_detail-title col-3">Date</section>
<section class="esh-orders_detail-title col-3">Total</section>
<section class="esh-orders_detail-title col-3">Status</section>
<section class="esh-orders_detail-title col-2">Order number</section>
<section class="esh-orders_detail-title col-2">Date</section>
<section class="esh-orders_detail-title col-2">Total</section>
<section class="esh-orders_detail-title col-2">Status</section>
<section class="esh-orders_detail-title col-2">RFID Scanned</section>
</article>
<article class="esh-orders_detail-items row">
<section class="esh-orders_detail-item col-3">@Model.OrderNumber</section>
<section class="esh-orders_detail-item col-3">@Model.Date</section>
<section class="esh-orders_detail-item col-3">$@Model.Total</section>
<section class="esh-orders_detail-title col-3">@Model.Status</section>
<section class="esh-orders_detail-item col-2">$@Model.Total</section>
<section class="esh-orders_detail-item col-2">@Model.OrderNumber</section>
<section class="esh-orders_detail-item col-2">@Model.Date</section>
<section class="esh-orders_detail-title col-2">@Model.Status</section>
<section class="esh-orders_detail-title col-2">@rfidScanned</section>
</article>
</section>
<section class="esh-orders_detail-section">
<article class="esh-orders_detail-titles row">
<section class="esh-orders_detail-title col-12">Description</section>
@ -88,4 +93,4 @@
</article>
</section>
</div>
</div>
</div>

+ 39
- 11
src/Web/WebMVC/Views/Order/Index.cshtml View File

@ -1,35 +1,63 @@
@using Microsoft.eShopOnContainers.WebMVC.ViewModels
@using Microsoft.eShopOnContainers.WebMVC.ViewModels.Customisation
@model IEnumerable<Microsoft.eShopOnContainers.WebMVC.ViewModels.Order>
@{
ViewData["Title"] = "My Orders";
var headerList= new List<Header>() {
ViewData["Title"] = "My Orders";
var headerList = new List<Header>() {
new Header() { Controller = "Catalog", Text = "Back to catalog" },
new Header() { Text = " / " },
new Header() { Controller = "OrderManagement", Text = "Orders Management" } };
var shippingInfo = ViewData["ShippingInfo"] as List<ShippingInformation>;
}
<div class="esh-orders">
<partial name="_Header" model="headerList"/>
<partial name="_Header" model="headerList" />
<div class="container">
<article class="esh-orders-titles row">
<section class="esh-orders-title col-2">Order number</section>
<section class="esh-orders-title col-4">Date</section>
<section class="esh-orders-title col-2">Total</section>
<section class="esh-orders-title col-2">Status</section>
<section class="esh-orders-title col-1">Order number</section>
<section class="esh-orders-title col-2">Date</section>
<section class="esh-orders-title col-1">Total</section>
<section class="esh-orders-title col-1">Status</section>
<section class="esh-orders-title col-2">Shipping date</section>
<section class="esh-orders-title col-2">Estimated arrival date</section>
<section class="esh-orders-title col-2"></section>
</article>
@if (Model != null && Model.Any())
{
foreach (var item in Model)
{
<article class="esh-orders-items row">
<section class="esh-orders-item col-2">@Html.DisplayFor(modelItem => item.OrderNumber)</section>
<section class="esh-orders-item col-4">@Html.DisplayFor(modelItem => item.Date)</section>
<section class="esh-orders-item col-2">$ @Html.DisplayFor(modelItem => item.Total)</section>
<section class="esh-orders-item col-2">@Html.DisplayFor(modelItem => item.Status)</section>
<section class="esh-orders-item col-1">@Html.DisplayFor(modelItem => item.OrderNumber)</section>
<section class="esh-orders-item col-2">@item.Date.ToShortDateString()</section>
<section class="esh-orders-item col-1">$ @Html.DisplayFor(modelItem => item.Total)</section>
<section class="esh-orders-item col-1">@Html.DisplayFor(modelItem => item.Status)</section>
<section class="esh-orders-item col-2">
@for (var i = 0; i < shippingInfo.Count(); i++)
{
var si = shippingInfo[i];
if (si.OrderNumber.Equals(item.OrderNumber))
{
@si.ShippingTime.ToShortDateString();
break;
}
}
</section>
<section class="esh-orders-item col-2">
@for (var i = 0; i < shippingInfo.Count(); i++)
{
var si = shippingInfo[i];
if (si.OrderNumber.Equals(item.OrderNumber))
{
@si.ArrivalTime.ToShortDateString();
break;
}
}
</section>
<section class="esh-orders-item col-1">
<a class="esh-orders-link" asp-controller="Order" asp-action="Detail" asp-route-orderId="@item.OrderNumber">Detail</a>
</section>


Loading…
Cancel
Save