Browse Source

Merge from eShopOnContainers Dev

pull/235/head
Ramón Tomás 7 years ago
parent
commit
a3e63c11aa
437 changed files with 13789 additions and 1446 deletions
  1. +1
    -0
      .gitignore
  2. +13
    -13
      README.md
  3. +29
    -17
      cli-linux/build-bits-linux.sh
  4. +2
    -2
      cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1
  5. +11
    -0
      cli-windows/build-images.ps1
  6. +28
    -1
      docker-compose-windows.override.yml
  7. +13
    -1
      docker-compose-windows.prod.yml
  8. +29
    -6
      docker-compose-windows.yml
  9. +7
    -3
      docker-compose.ci.build.yml
  10. +56
    -0
      docker-compose.nobuild.yml
  11. +45
    -3
      docker-compose.override.yml
  12. +13
    -1
      docker-compose.prod.yml
  13. +60
    -1
      docker-compose.vs.debug.yml
  14. +41
    -1
      docker-compose.vs.release.yml
  15. +56
    -15
      docker-compose.yml
  16. BIN
      docs/Enterprise-Application-Patterns-using-XamarinForms.pdf
  17. BIN
      docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook).pdf
  18. +321
    -4
      eShopOnContainers-ServicesAndWebApps.sln
  19. +52
    -52
      eShopOnContainers.sln
  20. +5
    -0
      global.json
  21. BIN
      img/xamarin-enterprise-patterns-ebook-cover-small.png
  22. +29
    -0
      k8s/basket-data.yaml
  23. +140
    -55
      k8s/deploy.ps1
  24. +229
    -25
      k8s/deployments.yaml
  25. +7
    -4
      k8s/gen-k8s-env.ps1
  26. +29
    -0
      k8s/keystore-data.yaml
  27. +31
    -0
      k8s/local.json
  28. +18
    -0
      k8s/nginx.conf
  29. +30
    -0
      k8s/nosql-data.yaml
  30. +43
    -1
      k8s/services.yaml
  31. +8
    -0
      src/BuildingBlocks/CommandBus/CommandBus/CommandBus.csproj
  32. +13
    -0
      src/BuildingBlocks/CommandBus/CommandBus/ICommandBus.cs
  33. +18
    -0
      src/BuildingBlocks/CommandBus/CommandBus/IntegrationCommand.cs
  34. +7
    -0
      src/BuildingBlocks/CommandBus/CommandBusRabbitMQ/CommandBusRabbitMQ.csproj
  35. +13
    -0
      src/BuildingBlocks/DataProtection/DataProtection/DataProtection.csproj
  36. +95
    -0
      src/BuildingBlocks/DataProtection/DataProtection/DataProtectionBuilderExtensions.cs
  37. +210
    -0
      src/BuildingBlocks/DataProtection/DataProtection/RedisXmlRepository.cs
  38. +8
    -0
      src/BuildingBlocks/EventBus/CommandBus/CommandBus.csproj
  39. +16
    -0
      src/BuildingBlocks/EventBus/CommandBus/ICommandBus.cs
  40. +16
    -0
      src/BuildingBlocks/EventBus/CommandBus/IIntegrationCommandHandler.cs
  41. +35
    -0
      src/BuildingBlocks/EventBus/CommandBus/IntegrationCommand.cs
  42. +5
    -5
      src/BuildingBlocks/EventBus/EventBus.Tests/InMemory_SubscriptionManager_Tests.cs
  43. +13
    -0
      src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs
  44. +7
    -1
      src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs
  45. +1
    -2
      src/BuildingBlocks/EventBus/EventBus/EventBus.csproj
  46. +14
    -6
      src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs
  47. +84
    -37
      src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs
  48. +28
    -0
      src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs
  49. +145
    -0
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/CommandBusRabbitMQ.cs
  50. +49
    -28
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
  51. +6
    -6
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj
  52. +7
    -8
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj
  53. +2
    -2
      src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/Microsoft.AspNetCore.HealthChecks.csproj
  54. +1
    -1
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/Microsoft.Extensions.HealthChecks.SqlServer.csproj
  55. +2
    -2
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Microsoft.Extensions.HealthChecks.csproj
  56. +3
    -3
      src/BuildingBlocks/Resilience/Resilience.Http/Resilience.Http.csproj
  57. +5
    -5
      src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs
  58. +6
    -6
      src/Mobile/README.md
  59. +41
    -4
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs
  60. +22
    -10
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs
  61. +34
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/ServicesHelper.cs
  62. +38
    -32
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs
  63. +36
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Basket/BasketCheckout.cs
  64. +8
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Location/Location.cs
  65. +19
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Marketing/Campaign.cs
  66. +19
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Marketing/CampaignItem.cs
  67. +12
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Marketing/CampaignRoot.cs
  68. +22
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Token/UserToken.cs
  69. +6
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketMockService.cs
  70. +9
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketService.cs
  71. +1
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/IBasketService.cs
  72. +3
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Common/Common.cs
  73. +5
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Identity/IIdentityService.cs
  74. +24
    -6
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Identity/IdentityService.cs
  75. +10
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/ILocationService.cs
  76. +28
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs
  77. +53
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignMockService.cs
  78. +53
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignService.cs
  79. +14
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/ICampaignService.cs
  80. +4
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/IOrderService.cs
  81. +17
    -10
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderMockService.cs
  82. +18
    -12
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderService.cs
  83. +2
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/IRequestProvider.cs
  84. +33
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/RequestProvider.cs
  85. +12
    -5
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs
  86. +42
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CampaignDetailsViewModel.cs
  87. +49
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CampaignViewModel.cs
  88. +4
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CheckoutViewModel.cs
  89. +13
    -9
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs
  90. +146
    -16
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs
  91. +113
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CampaignDetailsView.xaml
  92. +12
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CampaignDetailsView.xaml.cs
  93. +101
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CampaignView.xaml
  94. +13
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CampaignView.xaml.cs
  95. +11
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml
  96. +5
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml.cs
  97. +78
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/SettingsView.xaml
  98. +83
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/Templates/CampaignTemplate.xaml
  99. +12
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/Templates/CampaignTemplate.xaml.cs
  100. +45
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj

+ 1
- 0
.gitignore View File

@ -258,3 +258,4 @@ pub/
#Ignore marker-file used to know which docker files we have.
.eshopdocker_*
/src/Web/WebMVC/wwwroot/lib
/src/Web/WebMVC/wwwroot/css/site.min.css

+ 13
- 13
README.md View File

@ -3,20 +3,20 @@ Sample .NET Core reference application, powered by Microsoft, based on a simplif
**Note for Pull Requests**: We accept pull request from the community. When doing it, please do it onto the DEV branch which is the consolidated work-in-progress branch. Do not request it onto Master, if possible.
> ### DISCLAIMER
> **IMPORTANT:** The current state of this sample application is **BETA**, consider it version a 0.1 foundational version, therefore, many areas could be improved and change significantly while refactoring current code and implementing new features. **Feedback with improvements and pull requests from the community will be highly appreciated and accepted.**
> **IMPORTANT:** The current state of this sample application is **BETA**, consider it version a 0.1 foundational version, therefore, many areas could be improved and change significantly while refactoring current code and implementing new features. **Feedback with improvements and pull requests from the community will be highly appreciated and accepted.**
>
> This reference application proposes a simplified microservice oriented architecture implementation to introduce technologies like .NET Core with Docker containers through a comprehensive application. The chosen domain is an eShop/eCommerce but simply because it is a well-know domain by most people/developers.
However, this sample application should not be considered as an "eCommerce reference model", at all. The implemented business domain might not be ideal from an eCommerce business point of view. It is neither trying to solve all the problems in a large, scalable and mission-critical distributed system. It is just a bootstrap for developers to easily get started in the world of Docker containers and microservices with .NET Core.
> <p>For example, the next step (still not covered in eShopOnContainers) after understanding Docker containers and microservices development with .NET Core, is to select a microservice cluster/orchestrator like Docker Swarm, Kubernetes or DC/OS (in Azure Container Service) or Azure Service Fabric which in most of the cases will require additional partial changes to your application's configuration (although the present architecture should work on most orchestrators with small changes).
However, this sample application should not be considered as an "eCommerce reference model", at all. The implemented business domain might not be ideal from an eCommerce business point of view. It is neither trying to solve all the problems in a large, scalable and mission-critical distributed system. It is just a bootstrap for developers to easily get started in the world of Docker containers and microservices with .NET Core.
> <p>For example, the next step (still not covered in eShopOnContainers) after understanding Docker containers and microservices development with .NET Core, is to select a microservice cluster/orchestrator like Docker Swarm, Kubernetes or DC/OS (in Azure Container Service) or Azure Service Fabric which in most of the cases will require additional partial changes to your application's configuration (although the present architecture should work on most orchestrators with small changes).
> Additional steps would be to move your databases to HA cloud services, or to implement your EventBus with Azure Service Bus or any other production ready Service Bus in the market.
> <p> In the future we might fork this project and make multiple versions targeting specific microservice cluster/orchestrators plus using additional cloud infrastructure. <p>
> <img src="img/exploring-to-production-ready.png">
> Read the planned <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>Roadmap and Milestones for future releases of eShopOnContainers</a> within the Wiki for further info about possible new implementations and provide feedback at the <a href='https://github.com/dotnet/eShopOnContainers/issues'>ISSUES section</a> if you'd like to see any specific scenario implemented or improved. Also, feel free to discuss on any current issue.
**Architecture overview**: This reference application is cross-platform either at the server and client side, thanks to .NET Core services capable of running on Linux or Windows containers depending on your Docker host, and to Xamarin for mobile apps running on Android, iOS or Windows/UWP plus any browser for the client web apps.
The architecture proposes a simplified microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using Http as the current communication protocol.
**Architecture overview**: This reference application is cross-platform either at the server and client side, thanks to .NET Core services capable of running on Linux or Windows containers depending on your Docker host, and to Xamarin for mobile apps running on Android, iOS or Windows/UWP plus any browser for the client web apps.
The architecture proposes a simplified microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using Http as the current communication protocol.
<p>
It also supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus plus other features defined at the <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>roadmap</a>.
It also supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus plus other features defined at the <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>roadmap</a>.
<p>
<img src="img/eshop_logo.png">
<img src="img/eShopOnContainers_Architecture_Diagram.png">
@ -43,7 +43,7 @@ You can download them and start reviewing these Guides/eBooks here:
| Architecting & Developing | Containers Lifecycle & CI/CD | App patterns with Xamarin.Forms |
| ------------ | ------------| ------------|
| <a href='https://aka.ms/microservicesebook'><img src="img/ebook_arch_dev_microservices_containers_cover.png"> </a> | <a href='https://aka.ms/dockerlifecycleebook'> <img src="img/ebook_containers_lifecycle.png"> </a> | <a href='https://aka.ms/xamarinpatternsebook'> <img src="img/xamarin-enterprise-patterns-ebook-cover-small.png"> </a> |
| <sup> <a href='https://aka.ms/microservicesebook'>**Download** (First Edition)</a> </sup> | <sup> <a href='https://aka.ms/dockerlifecycleebook'>**Download** (First Edition from late 2016) </a> </sup> | <sup> <a href='https://aka.ms/xamarinpatternsebook'>**Download** (Preview Edition) </a> </sup> |
| <sup> <a href='https://aka.ms/microservicesebook'>**Download** (First Edition)</a> </sup> | <sup> <a href='https://aka.ms/dockerlifecycleebook'>**Download** (First Edition from late 2016) </a> </sup> | <sup> <a href='https://aka.ms/xamarinpatternsebook'>**Download** (First Edition) </a> </sup> |
Send feedback to [dotnet-architecture-ebooks-feedback@service.microsoft.com](dotnet-architecture-ebooks-feedback@service.microsoft.com)
<p>
@ -66,7 +66,7 @@ Finally, those microservices are consumed by multiple client web and mobile apps
<b>*MVC Application (ASP.NET Core)*</b>: Its an MVC application where you can find interesting scenarios on how to consume HTTP-based microservices from C# running in the server side, as it is a typical ASP.NET Core MVC application. Since it is a server-side application, access to other containers/microservices is done within the internal Docker Host network with its internal name resolution.
<img src="img/eshop-webmvc-app-screenshot.png">
<br>
<b>*SPA (Single Page Application)*</b>: Providing similar "eShop business functionality" but developed with Angular 2, Typescript and slightly using ASP.NET Core MVC. This is another approach for client web applications to be used when you want to have a more modern client behavior which is not behaving with the typical browser round-trip on every action but behaving like a Single-Page-Application which is more similar to a desktop app usage experience. The consumption of the HTTP-based microservices is done from TypeScript/JavaScript in the client browser, so the client calls to the microservices come from out of the Docker Host internal network (Like from your network or even from the Internet).
<b>*SPA (Single Page Application)*</b>: Providing similar "eShop business functionality" but developed with Angular 2, Typescript and slightly using ASP.NET Core MVC. This is another approach for client web applications to be used when you want to have a more modern client behavior which is not behaving with the typical browser round-trip on every action but behaving like a Single-Page-Application which is more similar to a desktop app usage experience. The consumption of the HTTP-based microservices is done from TypeScript/JavaScript in the client browser, so the client calls to the microservices come from out of the Docker Host internal network (Like from your network or even from the Internet).
<img src="img/eshop-webspa-app-screenshot.png">
<br>
<b>*Xamarin Mobile App (For iOS, Android and Windows/UWP)*</b>: It is a client mobile app supporting the most common mobile OS platforms (iOS, Android and Windows/UWP). In this case, the consumption of the microservices is done from C# but running on the client devices, so out of the Docker Host internal network (Like from your network or even the Internet).
@ -76,19 +76,19 @@ Finally, those microservices are consumed by multiple client web and mobile apps
## Setting up your development environment for eShopOnContainers
### Visual Studio 2017 and Windows based
This is the more straightforward way to get started:
https://github.com/dotnet/eShopOnContainers/wiki/02.-Setting-eShopOnContainer-solution-up-in-a-Visual-Studio-2017-environment
https://github.com/dotnet-architecture/eShopOnContainers/wiki/02.-Setting-eShopOnContainers-in-a-Visual-Studio-2017-environment
### CLI and Windows based
For those who prefer the CLI on Windows, using dotnet CLI, docker CLI and VS Code for Windows:
For those who prefer the CLI on Windows, using dotnet CLI, docker CLI and VS Code for Windows:
https://github.com/dotnet/eShopOnContainers/wiki/03.-Setting-the-eShopOnContainers-solution-up-in-a-Windows-CLI-environment-(dotnet-CLI,-Docker-CLI-and-VS-Code)
### CLI and Mac based
For those who prefer the CLI on a Mac, using dotnet CLI, docker CLI and VS Code for Mac
For those who prefer the CLI on a Mac, using dotnet CLI, docker CLI and VS Code for Mac
(Instructions still TBD, but similar to Windows CLI):
https://github.com/dotnet/eShopOnContainers/wiki/04.-Setting-eShopOnContainer-solution-up-in-a-Mac,-VS-Code-and-CLI-environment--(dotnet-CLI,-Docker-CLI-and-VS-Code)
> ### Note on tested Docker Containers/Images
> Most of the development and testing of this project was (as of early March 2017) done <b> on Docker Linux containers</b> running in development machines with "Docker for Windows" and the default Hyper-V Linux VM (MobiLinuxVM) installed by "Docker for Windows".
> Most of the development and testing of this project was (as of early March 2017) done <b> on Docker Linux containers</b> running in development machines with "Docker for Windows" and the default Hyper-V Linux VM (MobiLinuxVM) installed by "Docker for Windows".
The <b>Windows Containers scenario is currently being implemented/tested yet</b>. The application should be able to run on Windows Nano Containers based on different Docker base images, as well, as the .NET Core services have also been tested running on plain Windows (with no Docker).
The app was also partially tested on "Docker for Mac" using a development MacOS machine with .NET Core and VS Code installed, which is still a scenario using Linux containers running on the VM setup in the Mac by the "Docker for Windows" setup. But further testing and feedback on Mac environments and Windows Containers, from the community, will be appreciated.
@ -118,4 +118,4 @@ You can create new issues at the issues section, do pull requests and/or send em
## Questions
[QUESTION] Answer +1 if the solution is working for you (Through VS2017 or CLI environment):
https://github.com/dotnet/eShopOnContainers/issues/107
https://github.com/dotnet/eShopOnContainers/issues/107

+ 29
- 17
cli-linux/build-bits-linux.sh View File

@ -1,12 +1,23 @@
#!/bin/bash
declare -x path=$1
if [ -z "$path" ]; then
$path="$(pwd)/../src";
fi
declare -a projectList=(
'../src/Services/Catalog/Catalog.API'
'../src/Services/Basket/Basket.API'
'../src/Services/Ordering/Ordering.API'
'../src/Services/Identity/Identity.API'
'../src/Web/WebMVC'
'../src/Web/WebSPA'
'../src/Web/WebStatus'
"$path/Web/WebSPA"
"$path/Services/Catalog/Catalog.API"
"$path/Services/Basket/Basket.API"
"$path/Services/Ordering/Ordering.API"
"$path/Services/Identity/Identity.API"
"$path/Services/Location/Locations.API"
"$path/Services/Marketing/Marketing.API"
"$path/Services/Payment/Payment.API"
"$path/Services/GracePeriod/GracePeriodManager"
"$path/Web/WebMVC"
"$path/Web/WebStatus"
)
# Build SPA app
@ -15,9 +26,9 @@ declare -a projectList=(
for project in "${projectList[@]}"
do
echo -e "\e[33mWorking on $(pwd)/$project"
echo -e "\e[33mWorking on $path/$project"
echo -e "\e[33m\tRemoving old publish output"
pushd $(pwd)/$project
pushd $path/$project
rm -rf obj/Docker/publish
echo -e "\e[33m\tRestoring project"
dotnet restore
@ -26,14 +37,15 @@ do
popd
done
# remove old docker images:
images=$(docker images --filter=reference="eshop/*" -q)
if [ -n "$images" ]; then
docker rm $(docker ps -a -q) -f
echo "Deleting eShop images in local Docker repo"
echo $images
docker rmi $(docker images --filter=reference="eshop/*" -q) -f
fi
## remove old docker images:
#images=$(docker images --filter=reference="eshop/*" -q)
#if [ -n "$images" ]; then
# docker rm $(docker ps -a -q) -f
# echo "Deleting eShop images in local Docker repo"
# echo $images
# docker rmi $(docker images --filter=reference="eshop/*" -q) -f
#fi
# No need to build the images, docker build or docker compose will
# do that using the images and containers defined in the docker-compose.yml file.

+ 2
- 2
cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 View File

@ -21,6 +21,6 @@ try {
Write-Host "Rule found"
}
catch [Exception] {
New-NetFirewallRule -DisplayName eShopOnContainers-Inbound -Confirm -Description "eShopOnContainers Inbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5105 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Inbound
New-NetFirewallRule -DisplayName eShopOnContainers-Outbound -Confirm -Description "eShopOnContainers Outbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5105 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Outbound
New-NetFirewallRule -DisplayName eShopOnContainers-Inbound -Confirm -Description "eShopOnContainers Inbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5110 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Inbound
New-NetFirewallRule -DisplayName eShopOnContainers-Outbound -Confirm -Description "eShopOnContainers Outbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5110 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Outbound
}

+ 11
- 0
cli-windows/build-images.ps1 View File

@ -0,0 +1,11 @@
Param([string] $imageTag)
$scriptPath = Split-Path $script:MyInvocation.MyCommand.Path
if ([string]::IsNullOrEmpty($imageTag)) {
$imageTag = $(git rev-parse --abbrev-ref HEAD)
}
Write-Host "Building images with tag $imageTag" -ForegroundColor Yellow
$env:TAG=$imageTag
docker-compose -f "$scriptPath\..\docker-compose.yml" build

+ 28
- 1
docker-compose-windows.override.yml View File

@ -80,4 +80,31 @@ services:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y
ports:
- "5433:1433"
- "5433:1433"
nosql.data:
ports:
- "27017:27017"
locations.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=mongodb://nosql.data
- Database=LocationsDb
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5109:80"
marketing.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word
- EventBusConnection=rabbitmq
- MongoConnectionString=mongodb://nosql.data
- MongoDatabase=MarketingDb
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
ports:
- "5110:80"

+ 13
- 1
docker-compose-windows.prod.yml View File

@ -75,7 +75,19 @@ services:
- BasketUrl=http://basket.api
ports:
- "5100:80"
marketing.api:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word
- EventBusConnection=rabbitmq
- MongoConnectionString=mongodb://nosql.data
- MongoDatabase=MarketingDb
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
ports:
- "5110:80"
sql.data:
environment:
- SA_PASSWORD=Pass@word


+ 29
- 6
docker-compose-windows.yml View File

@ -2,7 +2,7 @@ version: '2.1'
services:
basket.api:
image: eshop/basket.api-win
image: eshop/basket.api-win:${TAG:-latest}
build:
context: ./src/Services/Basket/Basket.API
dockerfile: Dockerfile.nanowin
@ -11,7 +11,7 @@ services:
- identity.api
catalog.api:
image: eshop/catalog.api-win
image: eshop/catalog.api-win:${TAG:-latest}
build:
context: ./src/Services/Catalog/Catalog.API
dockerfile: Dockerfile.nanowin
@ -19,7 +19,7 @@ services:
- sql.data
identity.api:
image: eshop/identity.api-win
image: eshop/identity.api-win:${TAG:-latest}
build:
context: ./src/Services/Identity/Identity.API
dockerfile: Dockerfile.nanowin
@ -27,7 +27,7 @@ services:
- sql.data
ordering.api:
image: eshop/ordering.api-win
image: eshop/ordering.api-win:${TAG:-latest}
build:
context: ./src/Services/Ordering/Ordering.API
dockerfile: Dockerfile.nanowin
@ -35,7 +35,7 @@ services:
- sql.data
webspa:
image: eshop/webspa-win
image: eshop/webspa-win:${TAG:-latest}
build:
context: ./src/Web/WebSPA
dockerfile: Dockerfile.nanowin
@ -44,7 +44,7 @@ services:
- basket.api
webmvc:
image: eshop/webmvc-win
image: eshop/webmvc-win:${TAG:-latest}
build:
context: ./src/Web/WebMVC
dockerfile: Dockerfile.nanowin
@ -53,9 +53,32 @@ services:
- ordering.api
- identity.api
- basket.api
locations.api:
image: eshop/locations.api:${TAG:-latest}
build:
context: ./src/Services/Location/Locations.API
dockerfile: Dockerfile
depends_on:
- nosql.data
- rabbitmq
marketing.api:
image: eshop/marketing.api:${TAG:-latest}
build:
context: ./src/Services/Marketing/Marketing.API
dockerfile: Dockerfile
depends_on:
- sql.data
- nosql.data
- identity.api
- rabbitmq
sql.data:
image: microsoft/mssql-server-windows
nosql.data:
image: mongo:windowsservercore
basket.data:
image: redis:nanoserver


+ 7
- 3
docker-compose.ci.build.yml View File

@ -1,10 +1,14 @@
version: '2'
version: '3'
services:
ci-build:
image: microsoft/aspnetcore-build:1.0-1.1
image: microsoft/aspnetcore-build:1.1.2
volumes:
- .:/src
- ./cli-linux:/cli-linux
working_dir: /src
command: /bin/bash -c "pushd ./src/Web/WebSPA && npm rebuild node-sass && popd && dotnet restore ./eShopOnContainers-ServicesAndWebApps.sln && dotnet publish ./eShopOnContainers-ServicesAndWebApps.sln -c Release -o ./obj/Docker/publish"
# DO NOT USE the sln file to compile because msbuild issue (https://github.com/Microsoft/msbuild/issues/2153)
# command: /bin/bash -c "pushd ./src/Web/WebSPA && npm rebuild node-sass && popd && dotnet restore ./eShopOnContainers-ServicesAndWebApps.sln && dotnet publish ./eShopOnContainers-ServicesAndWebApps.sln -c Release -o ./obj/Docker/publish"
# NOTE: Using build-bits-linux.sh triggers the same MSBUILD error :( (but at least, less frequently)
command: /bin/bash -c "pushd ./src/Web/WebSPA && npm rebuild node-sass && popd && pushd /cli-linux && ./build-bits-linux.sh /src"

+ 56
- 0
docker-compose.nobuild.yml View File

@ -0,0 +1,56 @@
version: '2'
services:
basket.api:
image: eshop/basket.api
depends_on:
- basket.data
- identity.api
- rabbitmq
catalog.api:
image: eshop/catalog.api
depends_on:
- sql.data
- rabbitmq
identity.api:
image: eshop/identity.api
depends_on:
- sql.data
ordering.api:
image: eshop/ordering.api
depends_on:
- sql.data
webspa:
image: eshop/webspa
depends_on:
- identity.api
- basket.api
webmvc:
image: eshop/webmvc
depends_on:
- catalog.api
- ordering.api
- identity.api
- basket.api
sql.data:
image: microsoft/mssql-server-linux
basket.data:
image: redis
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq
ports:
- "5672:5672"
webstatus:
image: eshop/webstatus

+ 45
- 3
docker-compose.override.yml View File

@ -1,4 +1,4 @@
version: '2.1'
version: '3'
# The default docker-compose.override file can use the "localhost" as the external name for testing web apps within the same dev machine.
# The ESHOP_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like:
@ -7,7 +7,11 @@ version: '2.1'
# An external IP or DNS name has to be used (instead localhost and the 10.0.75.1 IP) when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance.
services:
graceperiodmanager:
environment:
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- EventBusConnection=rabbitmq
basket.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
@ -49,6 +53,19 @@ services:
ports:
- "5102:80"
marketing.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word
- MongoConnectionString=mongodb://nosql.data
- MongoDatabase=MarketingDb
- EventBusConnection=rabbitmq
- ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
ports:
- "5110:80"
webspa:
environment:
- ASPNETCORE_ENVIRONMENT=Development
@ -57,6 +74,7 @@ services:
- OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
- IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
- MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
@ -71,7 +89,8 @@ services:
- CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api
- BasketUrl=http://basket.api
- IdentityUrl=http://10.0.75.1:5105 #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser.
- IdentityUrl=http://10.0.75.1:5105
- MarketingUrl=http://marketing.api #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser.
#Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
ports:
- "5100:80"
@ -83,6 +102,10 @@ services:
ports:
- "5433:1433"
nosql.data:
ports:
- "27017:27017"
webstatus:
environment:
- ASPNETCORE_ENVIRONMENT=Development
@ -95,3 +118,22 @@ services:
- spa=http://webspa/hc
ports:
- "5107:80"
payment.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5108
- EventBusConnection=rabbitmq
ports:
- "5108:80"
locations.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=mongodb://nosql.data
- Database=LocationsDb
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5109:80"

+ 13
- 1
docker-compose.prod.yml View File

@ -1,4 +1,4 @@
version: '2.1'
version: '3'
# The Production docker-compose file has to have the external/real IPs or DNS names for the services
# The ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like:
@ -54,6 +54,18 @@ services:
ports:
- "5102:80"
marketing.api:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word
- MongoConnectionString=mongodb://nosql.data
- MongoDatabase=MarketingDb
- EventBusConnection=rabbitmq
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
ports:
- "5110:80"
webspa:
environment:
- ASPNETCORE_ENVIRONMENT=Production


+ 60
- 1
docker-compose.vs.debug.yml View File

@ -1,4 +1,4 @@
version: '2.1'
version: '3'
services:
basket.api:
@ -61,6 +61,22 @@ services:
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
marketing.api:
image: eshop/marketing.api:dev
build:
args:
source: ${DOCKER_BUILD_SOURCE}
environment:
- DOTNET_USE_POLLING_FILE_WATCHER=1
volumes:
- ./src/Services/Marketing/Marketing.API:/app
- ~/.nuget/packages:/root/.nuget/packages:ro
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
webspa:
image: eshop/webspa:dev
build:
@ -105,3 +121,46 @@ services:
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
payment.api:
image: eshop/payment.api:dev
build:
args:
source: ${DOCKER_BUILD_SOURCE}
environment:
- DOTNET_USE_POLLING_FILE_WATCHER=1
volumes:
- ./src/Services/Payment/Payment.API:/app
- ~/.nuget/packages:/root/.nuget/packages:ro
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
graceperiodmanager:
image: eshop/graceperiodmanager:dev
build:
args:
source: ${DOCKER_BUILD_SOURCE}
volumes:
- ./src/Services/GracePeriod/GracePeriodManager:/app
- ~/.nuget/packages:/root/.nuget/packages:ro
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
locations.api:
image: eshop/locations.api:dev
build:
args:
source: ${DOCKER_BUILD_SOURCE}
environment:
- DOTNET_USE_POLLING_FILE_WATCHER=1
volumes:
- ./src/Services/Location/Locations.API:/app
- ~/.nuget/packages:/root/.nuget/packages:ro
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"

+ 41
- 1
docker-compose.vs.release.yml View File

@ -1,4 +1,4 @@
version: '2.1'
version: '3'
services:
basket.api:
@ -41,6 +41,16 @@ services:
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
marketing.api:
build:
args:
source: ${DOCKER_BUILD_SOURCE}
volumes:
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
webspa:
build:
args:
@ -70,3 +80,33 @@ services:
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
payment.api:
build:
args:
source: ${DOCKER_BUILD_SOURCE}
volumes:
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
graceperiodmanager:
build:
args:
source: ${DOCKER_BUILD_SOURCE}
volumes:
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
locations.api:
build:
args:
source: ${DOCKER_BUILD_SOURCE}
volumes:
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"

+ 56
- 15
docker-compose.yml View File

@ -1,64 +1,88 @@
version: '2.1'
version: '3'
services:
graceperiodmanager:
image: eshop/graceperiodmanager:${TAG:-latest}
build:
context: ./src/Services/GracePeriod/GracePeriodManager
dockerfile: Dockerfile
depends_on:
- sql.data
- rabbitmq
basket.api:
image: eshop/basket.api
image: eshop/basket.api:${TAG:-latest}
build:
context: ./src/Services/Basket/Basket.API
dockerfile: Dockerfile
dockerfile: Dockerfile
depends_on:
- basket.data
- identity.api
- rabbitmq
catalog.api:
image: eshop/catalog.api
image: eshop/catalog.api:${TAG:-latest}
build:
context: ./src/Services/Catalog/Catalog.API
dockerfile: Dockerfile
dockerfile: Dockerfile
depends_on:
- sql.data
- rabbitmq
identity.api:
image: eshop/identity.api
image: eshop/identity.api:${TAG:-latest}
build:
context: ./src/Services/Identity/Identity.API
dockerfile: Dockerfile
dockerfile: Dockerfile
depends_on:
- sql.data
ordering.api:
image: eshop/ordering.api
image: eshop/ordering.api:${TAG:-latest}
build:
context: ./src/Services/Ordering/Ordering.API
dockerfile: Dockerfile
dockerfile: Dockerfile
depends_on:
- sql.data
- rabbitmq
marketing.api:
image: eshop/marketing.api:${TAG:-latest}
build:
context: ./src/Services/Marketing/Marketing.API
dockerfile: Dockerfile
depends_on:
- sql.data
- nosql.data
- identity.api
- rabbitmq
webspa:
image: eshop/webspa
image: eshop/webspa:${TAG:-latest}
build:
context: ./src/Web/WebSPA
dockerfile: Dockerfile
dockerfile: Dockerfile
depends_on:
- identity.api
- basket.api
webmvc:
image: eshop/webmvc
image: eshop/webmvc:${TAG:-latest}
build:
context: ./src/Web/WebMVC
dockerfile: Dockerfile
dockerfile: Dockerfile
depends_on:
- catalog.api
- ordering.api
- identity.api
- basket.api
- marketing.api
sql.data:
image: microsoft/mssql-server-linux
nosql.data:
image: mongo
basket.data:
image: redis
ports:
@ -70,7 +94,24 @@ services:
- "5672:5672"
webstatus:
image: eshop/webstatus
image: eshop/webstatus:${TAG:-latest}
build:
context: ./src/Web/WebStatus
dockerfile: Dockerfile
payment.api:
image: eshop/payment.api:${TAG:-latest}
build:
context: ./src/Services/Payment/Payment.API
dockerfile: Dockerfile
depends_on:
- rabbitmq
locations.api:
image: eshop/locations.api:${TAG:-latest}
build:
context: ./src/Services/Location/Locations.API
dockerfile: Dockerfile
depends_on:
- nosql.data
- rabbitmq

BIN
docs/Enterprise-Application-Patterns-using-XamarinForms.pdf View File


BIN
docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook).pdf View File


+ 321
- 4
eShopOnContainers-ServicesAndWebApps.sln View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.6
VisualStudioVersion = 15.0.26430.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
EndProject
@ -70,11 +70,31 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resilience.Http", "src\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj", "{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E145D-1593-47EE-9608-8E323D3C63F5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj", "{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.SqlServer", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj", "{4BD76717-3102-4969-8C2C-BAAA3F0263B6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBus.Tests", "src\BuildingBlocks\EventBus\EventBus.Tests\EventBus.Tests.csproj", "{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBus.Tests", "src\BuildingBlocks\EventBus\EventBus.Tests\EventBus.Tests.csproj", "{4A980AC4-7205-46BF-8CCB-09E44D700FD4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GracePeriod", "GracePeriod", "{F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GracePeriodManager", "src\Services\GracePeriod\GracePeriodManager\GracePeriodManager.csproj", "{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Location", "Location", "{41139F64-4046-4F16-96B7-D941D96FA9C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Locations.API", "src\Services\Location\Locations.API\Locations.API.csproj", "{E7581357-FC34-474C-B8F5-307EE3CE05EF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataProtection", "DataProtection", "{88B22DBB-AA8F-4290-A454-2C109352C345}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataProtection", "src\BuildingBlocks\DataProtection\DataProtection\DataProtection.csproj", "{23A33F9B-7672-426D-ACF9-FF8436ADC81A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Marketing", "Marketing", "{A5260DE0-1FDD-467E-9CC1-A028AB081CEE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.API", "src\Services\Marketing\Marketing.API\Marketing.API.csproj", "{DF395F85-B010-465D-857A-7EBCC512C0C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBusServiceBus", "src\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj", "{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}"
EndProject
@ -860,6 +880,54 @@ Global
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|ARM.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhone.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x64.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x64.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x86.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.AppStore|x86.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|ARM.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|ARM.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhone.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x64.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x64.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x86.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Debug|x86.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|Any CPU.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|ARM.ActiveCfg = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|ARM.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhone.ActiveCfg = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhone.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.ActiveCfg = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.ActiveCfg = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.Build.0 = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -956,6 +1024,246 @@ Global
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x64.Build.0 = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.ActiveCfg = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.Build.0 = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|ARM.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|iPhone.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|x64.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|x64.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|x86.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.AppStore|x86.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|ARM.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|ARM.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|iPhone.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|x64.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|x64.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|x86.ActiveCfg = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Debug|x86.Build.0 = Debug|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|Any CPU.Build.0 = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|ARM.ActiveCfg = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|ARM.Build.0 = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|iPhone.ActiveCfg = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|iPhone.Build.0 = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|x64.ActiveCfg = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|x64.Build.0 = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|x86.ActiveCfg = Release|Any CPU
{4A980AC4-7205-46BF-8CCB-09E44D700FD4}.Release|x86.Build.0 = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|ARM.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|iPhone.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|x64.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|x64.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|x86.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.AppStore|x86.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|ARM.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|ARM.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|iPhone.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|x64.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|x64.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|x86.ActiveCfg = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Debug|x86.Build.0 = Debug|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|Any CPU.Build.0 = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|ARM.ActiveCfg = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|ARM.Build.0 = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|iPhone.ActiveCfg = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|iPhone.Build.0 = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|x64.ActiveCfg = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|x64.Build.0 = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|x86.ActiveCfg = Release|Any CPU
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47}.Release|x86.Build.0 = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|ARM.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|iPhone.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|x64.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|x64.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|x86.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|x86.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|ARM.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|ARM.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|iPhone.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|x64.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|x64.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|x86.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|x86.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|Any CPU.Build.0 = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|ARM.ActiveCfg = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|ARM.Build.0 = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|iPhone.ActiveCfg = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|iPhone.Build.0 = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x64.ActiveCfg = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x64.Build.0 = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x86.ActiveCfg = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x86.Build.0 = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|ARM.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhone.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x64.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x64.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x86.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x86.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|ARM.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|ARM.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhone.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x64.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x64.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x86.ActiveCfg = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x86.Build.0 = Debug|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|Any CPU.Build.0 = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|ARM.ActiveCfg = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|ARM.Build.0 = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhone.ActiveCfg = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhone.Build.0 = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x64.ActiveCfg = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x64.Build.0 = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x86.ActiveCfg = Release|Any CPU
{23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x86.Build.0 = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|ARM.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhone.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x64.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x64.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x86.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x86.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|ARM.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|ARM.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhone.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x64.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x64.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x86.ActiveCfg = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x86.Build.0 = Debug|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|Any CPU.Build.0 = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|ARM.ActiveCfg = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|ARM.Build.0 = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhone.ActiveCfg = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhone.Build.0 = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x64.ActiveCfg = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x64.Build.0 = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x86.ActiveCfg = Release|Any CPU
{DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x86.Build.0 = Release|Any CPU
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -1085,9 +1393,18 @@ Global
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
{FBF43D93-F2E7-4FF8-B4AB-186895949B88} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502} = {FBF43D93-F2E7-4FF8-B4AB-186895949B88}
{022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5}
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{4A980AC4-7205-46BF-8CCB-09E44D700FD4} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{F6E0F0DD-1400-43C3-B5E0-7CC325728C47} = {F38B4FF0-0B49-405A-B1B4-F7A5E3BC4C4E}
{41139F64-4046-4F16-96B7-D941D96FA9C6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{E7581357-FC34-474C-B8F5-307EE3CE05EF} = {41139F64-4046-4F16-96B7-D941D96FA9C6}
{88B22DBB-AA8F-4290-A454-2C109352C345} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{23A33F9B-7672-426D-ACF9-FF8436ADC81A} = {88B22DBB-AA8F-4290-A454-2C109352C345}
{A5260DE0-1FDD-467E-9CC1-A028AB081CEE} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{DF395F85-B010-465D-857A-7EBCC512C0C2} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE}
EndGlobalSection
EndGlobal

+ 52
- 52
eShopOnContainers.sln View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.12
VisualStudioVersion = 15.0.26430.6
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
EndProject
@ -97,14 +97,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Health
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj", "{EE65FA8B-1D87-4050-BC21-F305F2F8AE45}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.Data", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj", "{0E7AEB45-80F2-42B9-96BB-3414669E58AA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience", "{D13768ED-5AF1-4E09-96DD-FF6E7A2E5E06}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Resilience.Http", "src\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj", "{D92EB452-7A72-4B26-A8ED-0204CD376BC4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{23FB706A-2701-41E9-8BF9-28936001CA41}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.SqlServer", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj", "{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@ -1322,54 +1322,6 @@ Global
{EE65FA8B-1D87-4050-BC21-F305F2F8AE45}.Release|x64.Build.0 = Release|Any CPU
{EE65FA8B-1D87-4050-BC21-F305F2F8AE45}.Release|x86.ActiveCfg = Release|Any CPU
{EE65FA8B-1D87-4050-BC21-F305F2F8AE45}.Release|x86.Build.0 = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|ARM.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|iPhone.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|x64.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|x64.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|x86.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.AppStore|x86.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|ARM.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|ARM.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|iPhone.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|x64.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|x64.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|x86.ActiveCfg = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Debug|x86.Build.0 = Debug|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|Any CPU.Build.0 = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|ARM.ActiveCfg = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|ARM.Build.0 = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|iPhone.ActiveCfg = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|iPhone.Build.0 = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|x64.ActiveCfg = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|x64.Build.0 = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|x86.ActiveCfg = Release|Any CPU
{0E7AEB45-80F2-42B9-96BB-3414669E58AA}.Release|x86.Build.0 = Release|Any CPU
{D92EB452-7A72-4B26-A8ED-0204CD376BC4}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D92EB452-7A72-4B26-A8ED-0204CD376BC4}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D92EB452-7A72-4B26-A8ED-0204CD376BC4}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -1466,6 +1418,54 @@ Global
{23FB706A-2701-41E9-8BF9-28936001CA41}.Release|x64.Build.0 = Release|Any CPU
{23FB706A-2701-41E9-8BF9-28936001CA41}.Release|x86.ActiveCfg = Release|Any CPU
{23FB706A-2701-41E9-8BF9-28936001CA41}.Release|x86.Build.0 = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|ARM.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|iPhone.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|x64.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|x64.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|x86.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.AppStore|x86.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|ARM.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|ARM.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|iPhone.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|x64.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|x64.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|x86.ActiveCfg = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Debug|x86.Build.0 = Debug|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|Any CPU.Build.0 = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|ARM.ActiveCfg = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|ARM.Build.0 = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|iPhone.ActiveCfg = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|iPhone.Build.0 = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|x64.ActiveCfg = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|x64.Build.0 = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|x86.ActiveCfg = Release|Any CPU
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1510,9 +1510,9 @@ Global
{96CE8CE7-BC97-4A53-899F-5EB63D7BBF7B} = {1EF3AC0F-F27C-46DD-AC53-D762D2C11C45}
{FFFD3E09-A803-4F99-BAC5-C93ABA3E02D3} = {96CE8CE7-BC97-4A53-899F-5EB63D7BBF7B}
{EE65FA8B-1D87-4050-BC21-F305F2F8AE45} = {96CE8CE7-BC97-4A53-899F-5EB63D7BBF7B}
{0E7AEB45-80F2-42B9-96BB-3414669E58AA} = {96CE8CE7-BC97-4A53-899F-5EB63D7BBF7B}
{D13768ED-5AF1-4E09-96DD-FF6E7A2E5E06} = {1EF3AC0F-F27C-46DD-AC53-D762D2C11C45}
{D92EB452-7A72-4B26-A8ED-0204CD376BC4} = {D13768ED-5AF1-4E09-96DD-FF6E7A2E5E06}
{23FB706A-2701-41E9-8BF9-28936001CA41} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
{6CCC4F1B-602D-4FAD-91A7-002CC86C7612} = {96CE8CE7-BC97-4A53-899F-5EB63D7BBF7B}
EndGlobalSection
EndGlobal

+ 5
- 0
global.json View File

@ -0,0 +1,5 @@
{
"sdk": {
"version":"1.0.4"
}
}

BIN
img/xamarin-enterprise-patterns-ebook-cover-small.png View File

Before After
Width: 260  |  Height: 336  |  Size: 25 KiB Width: 260  |  Height: 336  |  Size: 38 KiB

+ 29
- 0
k8s/basket-data.yaml View File

@ -0,0 +1,29 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: basket-data
name: basket-data
spec:
ports:
- port: 6379
selector:
app: eshop
component: basket-data
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: basket-data
spec:
template:
metadata:
labels:
app: eshop
component: basket-data
spec:
containers:
- name: basket-data
image: redis:3.2-alpine

+ 140
- 55
k8s/deploy.ps1 View File

@ -2,10 +2,14 @@ Param(
[parameter(Mandatory=$false)][string]$registry,
[parameter(Mandatory=$false)][string]$dockerUser,
[parameter(Mandatory=$false)][string]$dockerPassword,
[parameter(Mandatory=$false)][bool]$deployCI,
[parameter(Mandatory=$false)][bool]$useDockerHub,
[parameter(Mandatory=$false)][string]$execPath,
[parameter(Mandatory=$false)][string]$kubeconfigPath
[parameter(Mandatory=$false)][string]$kubeconfigPath,
[parameter(Mandatory=$true)][string]$configFile,
[parameter(Mandatory=$false)][string]$imageTag,
[parameter(Mandatory=$false)][string]$externalDns,
[parameter(Mandatory=$false)][bool]$deployCI=$false,
[parameter(Mandatory=$false)][bool]$buildImages=$false,
[parameter(Mandatory=$false)][bool]$deployInfrastructure=$true
)
function ExecKube($cmd) {
@ -20,7 +24,11 @@ function ExecKube($cmd) {
}
}
# Not used when deploying through CI VSTS
# Initialization
$debugMode = $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent
$useDockerHub = [string]::IsNullOrEmpty($registry)
# Check required commands (only if not in CI environment)
if(-not $deployCI) {
$requiredCommands = ("docker", "docker-compose", "kubectl")
foreach ($command in $requiredCommands) {
@ -30,6 +38,45 @@ if(-not $deployCI) {
}
}
}
else {
$buildImages = false; # Never build images through CI, as they previously built
}
# Get tag to use from current branch if no tag is passed
if ([string]::IsNullOrEmpty($imageTag)) {
$imageTag = $(git rev-parse --abbrev-ref HEAD)
}
Write-Host "Docker image Tag: $imageTag" -ForegroundColor Yellow
# Read config to use
$config = Get-Content -Raw -Path $configFile | ConvertFrom-Json
if ($debugMode) {
Write-Host "[DEBUG]: Using following JSON config: " -ForegroundColor Yellow
$json = ConvertTo-Json $config -Depth 5
Write-Host $json
if (-not $deployCI) {
Write-Host "[DEBUG]: Press a key " -ForegroundColor Yellow
[System.Console]::Read()
}
}
# building and publishing docker images if needed
if($buildImages) {
Write-Host "Building and publishing eShopOnContainers..." -ForegroundColor Yellow
dotnet restore ../eShopOnContainers-ServicesAndWebApps.sln
dotnet publish -c Release -o obj/Docker/publish ../eShopOnContainers-ServicesAndWebApps.sln
Write-Host "Building Docker images tagged with '$imageTag'" -ForegroundColor Yellow
$env:TAG=$imageTag
docker-compose -p .. -f ../docker-compose.yml build
Write-Host "Pushing images to $registry..." -ForegroundColor Yellow
$services = ("basket.api", "catalog.api", "identity.api", "ordering.api", "marketing.api","payment.api","locations.api", "webmvc", "webspa", "webstatus")
foreach ($service in $services) {
docker tag eshop/${service}:$imageTag $registry/eshop/${service}:$imageTag
docker push $registry/eshop/${service}:$imageTag
}
}
# Use ACR instead of DockerHub as image repository
if(-not $useDockerHub) {
@ -50,77 +97,115 @@ if(-not $useDockerHub) {
# Removing previous services & deployments
Write-Host "Removing existing services & deployments.." -ForegroundColor Yellow
ExecKube -cmd 'delete -f sql-data.yaml -f rabbitmq.yaml'
ExecKube -cmd 'delete -f services.yaml -f frontend.yaml -f deployments.yaml'
ExecKube -cmd 'delete deployments --all'
ExecKube -cmd 'delete services --all'
ExecKube -cmd 'delete configmap config-files'
ExecKube -cmd 'delete configmap urls'
ExecKube -cmd 'delete configmap externalcfg'
# start sql, rabbitmq, frontend deploymentsExecKube -cmd 'delete configmap config-files'
ExecKube -cmd 'create configmap config-files --from-file=nginx-conf=nginx.conf'
ExecKube -cmd 'label configmap config-files app=eshop'
ExecKube -cmd 'create -f sql-data.yaml -f rabbitmq.yaml -f services.yaml -f frontend.yaml'
# building and publishing docker images not necessary when deploying through CI VSTS
if(-not $deployCI) {
Write-Host "Building and publishing eShopOnContainers..." -ForegroundColor Yellow
dotnet restore ../eShopOnContainers-ServicesAndWebApps.sln
dotnet publish -c Release -o obj/Docker/publish ../eShopOnContainers-ServicesAndWebApps.sln
Write-Host "Building Docker images..." -ForegroundColor Yellow
docker-compose -p .. -f ../docker-compose.yml build
Write-Host "Pushing images to $registry..." -ForegroundColor Yellow
$services = ("basket.api", "catalog.api", "identity.api", "ordering.api", "webmvc", "webspa", "webstatus")
foreach ($service in $services) {
docker tag eshop/$service $registry/eshop/$service
docker push $registry/eshop/$service
}
if ($deployInfrastructure) {
Write-Host 'Deploying infrastructure deployments (databases, redis, ...)' -ForegroundColor Yellow
ExecKube -cmd 'create -f sql-data.yaml -f basket-data.yaml -f keystore-data.yaml -f rabbitmq.yaml -f nosql-data.yaml'
}
Write-Host "Waiting for frontend's external ip..." -ForegroundColor Yellow
while ($true) {
$frontendUrl = & ExecKube -cmd 'get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"'
if ([bool]($frontendUrl -as [ipaddress])) {
break
}
Start-Sleep -s 15
Write-Host 'Deploying code deployments (databases, redis, ...)' -ForegroundColor Yellow
ExecKube -cmd 'create -f services.yaml -f frontend.yaml'
if ([string]::IsNullOrEmpty($externalDns)) {
Write-Host "Waiting for frontend's external ip..." -ForegroundColor Yellow
while ($true) {
$frontendUrl = & ExecKube -cmd 'get svc frontend -o=jsonpath="{.status.loadBalancer.ingress[0].ip}"'
if ([bool]($frontendUrl -as [ipaddress])) {
break
}
Start-Sleep -s 15
}
$externalDns = $frontendUrl
}
Write-Host "Using $externalDns as the external DNS/IP of the k8s cluster"
ExecKube -cmd 'create configmap urls `
--from-literal=BasketUrl=http://$($frontendUrl)/basket-api `
--from-literal=BasketHealthCheckUrl=http://$($frontendUrl)/basket-api/hc `
--from-literal=BasketUrl=http://basket `
--from-literal=BasketHealthCheckUrl=http://basket/hc `
--from-literal=CatalogUrl=http://$($frontendUrl)/catalog-api `
--from-literal=CatalogHealthCheckUrl=http://$($frontendUrl)/catalog-api/hc `
--from-literal=CatalogHealthCheckUrl=http://catalog/hc `
--from-literal=IdentityUrl=http://$($frontendUrl)/identity `
--from-literal=IdentityHealthCheckUrl=http://$($frontendUrl)/identity/hc `
--from-literal=OrderingUrl=http://$($frontendUrl)/ordering-api `
--from-literal=OrderingHealthCheckUrl=http://$($frontendUrl)/ordering-api/hc `
--from-literal=MvcClient=http://$($frontendUrl)/webmvc `
--from-literal=WebMvcHealthCheckUrl=http://$($frontendUrl)/webmvc/hc `
--from-literal=WebStatusClient=http://$($frontendUrl)/webstatus `
--from-literal=WebSpaHealthCheckUrl=http://$($frontendUrl)/hc `
--from-literal=SpaClient=http://$($frontendUrl)'
--from-literal=IdentityHealthCheckUrl=http://identity/hc `
--from-literal=OrderingUrl=http://ordering `
--from-literal=OrderingHealthCheckUrl=http://ordering/hc `
--from-literal=MvcClientExternalUrl=http://$($frontendUrl)/webmvc `
--from-literal=WebMvcHealthCheckUrl=http://webmvc/hc `
--from-literal=MvcClientOrderingUrl=http://ordering `
--from-literal=MvcClientCatalogUrl=http://catalog `
--from-literal=MvcClientBasketUrl=http://basket `
--from-literal=WebSpaHealthCheckUrl=http://webspa/hc `
--from-literal=SpaClientOrderingExternalUrl=http://$($externalDns)/ordering-api `
--from-literal=SpaClientCatalogExternalUrl=http://$($externalDns)/catalog-api `
--from-literal=SpaClientBasketExternalUrl=http://$($externalDns)/basket-api `
--from-literal=SpaClientIdentityExternalUrl=http://$($externalDns)/identity `
--from-literal=SpaClientExternalUrl=http://$($externalDns)'
ExecKube -cmd 'label configmap urls app=eshop'
Write-Host "Creating deployments..."
Write-Host "Applying external configuration from json" -ForegroundColor Yellow
ExecKube -cmd 'create configmap externalcfg `
--from-literal=CatalogSqlDb=$($config.sql.catalog) `
--from-literal=IdentitySqlDb=$($config.sql.identity) `
--from-literal=OrderingSqlDb=$($config.sql.ordering) `
--from-literal=MarketingSqlDb=$($config.sql.marketing) `
--from-literal=LocationsNoSqlDb=$($config.nosql.locations.constr) `
--from-literal=LocationsNoSqlDbName=$($config.nosql.locations.db) `
--from-literal=MarketingNoSqlDb=$($config.nosql.marketing.constr) `
--from-literal=MarketingNoSqlDbName=$($config.nosql.marketing.db) `
--from-literal=BasketRedisConStr=$($config.redis.basket) `
--from-literal=LocationsBus=$($config.servicebus.locations) `
--from-literal=MarketingBus=$($config.servicebus.marketing) `
--from-literal=BasketBus=$($config.servicebus.basket) `
--from-literal=OrderingBus=$($config.servicebus.ordering) `
--from-literal=CatalogBus=$($config.servicebus.catalog) `
--from-literal=PaymentBus=$($config.servicebus.payment) `
--from-literal=UseAzureServiceBus=$($config.servicebus.use_azure) `
--from-literal=keystore=$($config.redis.keystore) '
ExecKube -cmd 'label configmap externalcfg app=eshop'
Write-Host "Creating deployments..." -ForegroundColor Yellow
ExecKube -cmd 'create -f deployments.yaml'
# not using ACR for pulling images when deploying through CI VSTS
if(-not $deployCI) {
# update deployments with the private registry before k8s tries to pull images
# (deployment templating, or Helm, would obviate this)
ExecKube -cmd 'set image -f deployments.yaml `
basket=$registry/eshop/basket.api `
catalog=$registry/eshop/catalog.api `
identity=$registry/eshop/identity.api `
ordering=$registry/eshop/ordering.api `
webmvc=$registry/eshop/webmvc `
webstatus=$registry/eshop/webstatus `
webspa=$registry/eshop/webspa'
# update deployments with the correct image (with tag and/or registry)
Write-Host "Update Image containers to use prefix '$registry' and tag '$imageTag'" -ForegroundColor Yellow
$registryPath = ""
if (-not [string]::IsNullOrEmpty($registry)) {
$registryPath = "$registry/"
}
ExecKube -cmd 'rollout resume -f deployments.yaml'
ExecKube -cmd 'set image deployments/basket basket=${registryPath}eshop/basket.api:$imageTag'
ExecKube -cmd 'set image deployments/catalog catalog=${registryPath}eshop/catalog.api:$imageTag'
ExecKube -cmd 'set image deployments/identity identity=${registryPath}eshop/identity.api:$imageTag'
ExecKube -cmd 'set image deployments/ordering ordering=${registryPath}eshop/ordering.api:$imageTag'
ExecKube -cmd 'set image deployments/marketing marketing=${registryPath}eshop/marketing.api:$imageTag'
ExecKube -cmd 'set image deployments/locations locations=${registryPath}eshop/locations.api:$imageTag'
ExecKube -cmd 'set image deployments/payment payment=${registryPath}eshop/payment.api:$imageTag'
ExecKube -cmd 'set image deployments/webmvc webmvc=${registryPath}eshop/webmvc:$imageTag'
ExecKube -cmd 'set image deployments/webstatus webstatus=${registryPath}eshop/webstatus:$imageTag'
ExecKube -cmd 'set image deployments/webspa webspa=${registryPath}eshop/webspa:$imageTag'
Write-Host "Execute rollout..." -ForegroundColor Yellow
ExecKube -cmd 'rollout resume deployments/basket'
ExecKube -cmd 'rollout resume deployments/catalog'
ExecKube -cmd 'rollout resume deployments/identity'
ExecKube -cmd 'rollout resume deployments/ordering'
ExecKube -cmd 'rollout resume deployments/marketing'
ExecKube -cmd 'rollout resume deployments/locations'
ExecKube -cmd 'rollout resume deployments/payment'
ExecKube -cmd 'rollout resume deployments/webmvc'
ExecKube -cmd 'rollout resume deployments/webstatus'
ExecKube -cmd 'rollout resume deployments/webspa'
Write-Host "WebSPA is exposed at http://$frontendUrl, WebMVC at http://$frontendUrl/webmvc, WebStatus at http://$frontendUrl/webstatus" -ForegroundColor Yellow

+ 229
- 25
k8s/deployments.yaml View File

@ -18,20 +18,27 @@ spec:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/basket-api
- name: ConnectionString
value: 127.0.0.1
valueFrom:
configMapKeyRef:
name: externalcfg
key: BasketRedisConStr
- name: EventBusConnection
value: rabbitmq
valueFrom:
configMapKeyRef:
name: externalcfg
key: BasketBus
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
ports:
- containerPort: 80
- name: basket-data
image: redis:3.2-alpine
ports:
- containerPort: 6379
- containerPort: 80
imagePullSecrets:
- name: registry-key
---
@ -55,14 +62,20 @@ spec:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/catalog-api
- name: ConnectionString
value: "Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word"
- name: EventBusConnection
value: rabbitmq
valueFrom:
configMapKeyRef:
name: externalcfg
key: CatalogSqlDb
- name: ExternalCatalogBaseUrl
valueFrom:
configMapKeyRef:
name: urls
key: CatalogUrl
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: CatalogBus
ports:
- containerPort: 80
imagePullSecrets:
@ -88,17 +101,27 @@ spec:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/identity
- name: ConnectionStrings__DefaultConnection
value: "Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word"
valueFrom:
configMapKeyRef:
name: externalcfg
key: IdentitySqlDb
- name: DPConnectionString
valueFrom:
configMapKeyRef:
name: externalcfg
key: keystore
- name: IsClusterEnv
value: 'True'
- name: MvcClient
valueFrom:
configMapKeyRef:
name: urls
key: MvcClient
key: MvcClientExternalUrl
- name: SpaClient
valueFrom:
configMapKeyRef:
name: urls
key: SpaClient
key: SpaClientExternalUrl
ports:
- containerPort: 80
imagePullSecrets:
@ -124,9 +147,123 @@ spec:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/ordering-api
- name: ConnectionString
value: "Server=sql-data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;"
valueFrom:
configMapKeyRef:
name: externalcfg
key: OrderingSqlDb
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: OrderingBus
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
ports:
- containerPort: 80
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: locations
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: locations
spec:
containers:
- name: locations
image: eshop/locations.api
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/locations-api
- name: ConnectionString
valueFrom:
configMapKeyRef:
name: externalcfg
key: LocationsNoSqlDb
- name: Database
valueFrom:
configMapKeyRef:
name: externalcfg
key: LocationsNoSqlDbName
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: LocationsBus
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
ports:
- containerPort: 80
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: marketing
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: marketing
spec:
containers:
- name: marketing
image: eshop/marketing.api
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/marketing-api
- name: ConnectionString
valueFrom:
configMapKeyRef:
name: externalcfg
key: MarketingSqlDb
- name: MongoConnectionString
valueFrom:
configMapKeyRef:
name: externalcfg
key: MarketingNoSqlDb
- name: MongoDatabase
valueFrom:
configMapKeyRef:
name: externalcfg
key: MarketingNoSqlDbName
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
- name: EventBusConnection
value: rabbitmq
valueFrom:
configMapKeyRef:
name: externalcfg
key: MarketingBus
- name: IdentityUrl
valueFrom:
configMapKeyRef:
@ -139,6 +276,40 @@ spec:
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: payment
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: payment
spec:
containers:
- name: payment
image: eshop/payment.api
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/payment-api
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: PaymentBus
ports:
- containerPort: 80
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: webmvc
spec:
@ -156,21 +327,28 @@ spec:
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80/webmvc
- name: DPConnectionString
valueFrom:
configMapKeyRef:
name: externalcfg
key: keystore
- name: IsClusterEnv
value: 'True'
- name: BasketUrl
valueFrom:
configMapKeyRef:
name: urls
key: BasketUrl
key: MvcClientBasketUrl
- name: CallBackUrl
valueFrom:
configMapKeyRef:
name: urls
key: MvcClient
key: MvcClientExternalUrl
- name: CatalogUrl
valueFrom:
configMapKeyRef:
name: urls
key: CatalogUrl
key: MvcClientCatalogUrl
- name: IdentityUrl
valueFrom:
configMapKeyRef:
@ -180,13 +358,12 @@ spec:
valueFrom:
configMapKeyRef:
name: urls
key: OrderingUrl
key: MvcClientOrderingUrl
ports:
- containerPort: 80
imagePullSecrets:
- name: registry-key
---
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
@ -260,31 +437,58 @@ spec:
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80
- name: DPConnectionString
valueFrom:
configMapKeyRef:
name: externalcfg
key: keystore
- name: IsClusterEnv
value: 'True'
- name: BasketUrl
valueFrom:
configMapKeyRef:
name: urls
key: BasketUrl
key: SpaClientBasketExternalUrl
- name: CallBackUrl
valueFrom:
configMapKeyRef:
name: urls
key: SpaClient
key: SpaClientExternalUrl
- name: CatalogUrl
valueFrom:
configMapKeyRef:
name: urls
key: CatalogUrl
key: SpaClientCatalogExternalUrl
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
key: SpaClientIdentityExternalUrl
- name: OrderingUrl
valueFrom:
configMapKeyRef:
name: urls
key: OrderingUrl
key: SpaClientOrderingExternalUrl
- name: BasketUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: BasketHealthCheckUrl
- name: CatalogUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: CatalogHealthCheckUrl
- name: IdentityUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: IdentityHealthCheckUrl
- name: OrderingUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: OrderingHealthCheckUrl
ports:
- containerPort: 80
imagePullSecrets:


+ 7
- 4
k8s/gen-k8s-env.ps1 View File

@ -3,16 +3,19 @@
[parameter(Mandatory=$true)][string]$location,
[parameter(Mandatory=$true)][string]$registryName,
[parameter(Mandatory=$true)][string]$orchestratorName,
[parameter(Mandatory=$true)][string]$dnsName
[parameter(Mandatory=$true)][string]$dnsName,
[parameter(Mandatory=$true)][string]$createAcr=$true
)
# Create resource group
Write-Host "Creating resource group..." -ForegroundColor Yellow
az group create --name=$resourceGroupName --location=$location
# Create Azure Container Registry
Write-Host "Creating Azure Container Registry..." -ForegroundColor Yellow
az acr create -n $registryName -g $resourceGroupName -l $location --admin-enabled true --sku Basic
if ($createAcr) {
# Create Azure Container Registry
Write-Host "Creating Azure Container Registry..." -ForegroundColor Yellow
az acr create -n $registryName -g $resourceGroupName -l $location --admin-enabled true --sku Basic
}
# Create kubernetes orchestrator
Write-Host "Creating kubernetes orchestrator..." -ForegroundColor Yellow


+ 29
- 0
k8s/keystore-data.yaml View File

@ -0,0 +1,29 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: keystore-data
name: keystore-data
spec:
ports:
- port: 6379
selector:
app: eshop
component: keystore-data
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: keystore-data
spec:
template:
metadata:
labels:
app: eshop
component: keystore-data
spec:
containers:
- name: keystore-data
image: redis:3.2-alpine

+ 31
- 0
k8s/local.json View File

@ -0,0 +1,31 @@
{
"sql": {
"catalog": "Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word;",
"identity":"Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;",
"ordering":"Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;",
"marketing":"Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word;"
},
"nosql": {
"locations": {
"constr": "mongodb://nosql-data",
"db": "LocationsDb"
},
"marketing": {
"constr": "mongodb://nosql-data",
"db": "MarketingDb"
}
},
"redis": {
"basket" : "basket-data",
"keystore": "keystore-data"
},
"servicebus": {
"use_azure": false,
"ordering": "rabbitmq",
"marketing": "rabbitmq",
"locations": "rabbitmq",
"payment": "rabbitmq",
"basket": "rabbitmq",
"catalog": "rabbitmq"
}
}

+ 18
- 0
k8s/nginx.conf View File

@ -71,6 +71,24 @@ http {
proxy_set_header Host $host;
}
location /marketing-api {
proxy_pass http://marketing;
proxy_redirect off;
proxy_set_header Host $host;
}
location /payment-api {
proxy_pass http://payment;
proxy_redirect off;
proxy_set_header Host $host;
}
location /locations-api {
proxy_pass http://locations;
proxy_redirect off;
proxy_set_header Host $host;
}
location / {
proxy_pass http://webspa;
proxy_redirect off;


+ 30
- 0
k8s/nosql-data.yaml View File

@ -0,0 +1,30 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: nosql-data
name: nosql-data
spec:
ports:
- port: 27017
selector:
app: eshop
component: nosql-data
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nosql-data
spec:
template:
metadata:
labels:
app: eshop
component: nosql-data
spec:
containers:
- name: nosql-data
image: mongo
ports:
- containerPort: 27017

+ 43
- 1
k8s/services.yaml View File

@ -56,6 +56,48 @@ spec:
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: locations
name: locations
spec:
ports:
- port: 80
selector:
app: eshop
component: locations
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: payment
name: payment
spec:
ports:
- port: 80
selector:
app: eshop
component: payment
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: marketing
name: marketing
spec:
ports:
- port: 80
selector:
app: eshop
component: marketing
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
@ -94,4 +136,4 @@ spec:
- port: 80
selector:
app: eshop
component: webspa
component: webspa

+ 8
- 0
src/BuildingBlocks/CommandBus/CommandBus/CommandBus.csproj View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.CommandBus</RootNamespace>
</PropertyGroup>
</Project>

+ 13
- 0
src/BuildingBlocks/CommandBus/CommandBus/ICommandBus.cs View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
{
public interface ICommandBus
{
Task SendAsync<T>(T command) where T : IntegrationCommand;
}
}

+ 18
- 0
src/BuildingBlocks/CommandBus/CommandBus/IntegrationCommand.cs View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
{
public abstract class IntegrationCommand
{
public Guid Id { get; private set; }
public DateTime Sent { get; private set; }
protected IntegrationCommand()
{
Id = Guid.NewGuid();
Sent = DateTime.UtcNow;
}
}
}

+ 7
- 0
src/BuildingBlocks/CommandBus/CommandBusRabbitMQ/CommandBusRabbitMQ.csproj View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
</Project>

+ 13
- 0
src/BuildingBlocks/DataProtection/DataProtection/DataProtection.csproj View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="1.1.2" />
<PackageReference Include="StackExchange.Redis" Version="1.2.3" />
</ItemGroup>
</Project>

+ 95
- 0
src/BuildingBlocks/DataProtection/DataProtection/DataProtectionBuilderExtensions.cs View File

@ -0,0 +1,95 @@
namespace Microsoft.eShopOnContainers.BuildingBlocks
{
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Net;
/// <summary>
/// Extension methods for <see cref="IDataProtectionBuilder"/> for configuring
/// data protection options.
/// </summary>
public static class DataProtectionBuilderExtensions
{
/// <summary>
/// Sets up data protection to persist session keys in Redis.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/> used to set up data protection options.</param>
/// <param name="redisConnectionString">The connection string specifying the Redis instance and database for key storage.</param>
/// <returns>
/// The <paramref name="builder" /> for continued configuration.
/// </returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="builder" /> or <paramref name="redisConnectionString" /> is <see langword="null" />.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Thrown if <paramref name="redisConnectionString" /> is empty.
/// </exception>
public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, string redisConnectionString)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (redisConnectionString == null)
{
throw new ArgumentNullException(nameof(redisConnectionString));
}
if (redisConnectionString.Length == 0)
{
throw new ArgumentException("Redis connection string may not be empty.", nameof(redisConnectionString));
}
var ips = Dns.GetHostAddressesAsync(redisConnectionString).Result;
return builder.Use(ServiceDescriptor.Singleton<IXmlRepository>(services =>
new RedisXmlRepository(ips.First().ToString(), services.GetRequiredService<ILogger<RedisXmlRepository>>())));
}
/// <summary>
/// Updates an <see cref="IDataProtectionBuilder"/> to use the service of
/// a specific type, removing all other services of that type.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/> that should use the specified service.</param>
/// <param name="descriptor">The <see cref="ServiceDescriptor"/> with the service the <paramref name="builder" /> should use.</param>
/// <returns>
/// The <paramref name="builder" /> for continued configuration.
/// </returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="builder" /> or <paramref name="descriptor" /> is <see langword="null" />.
/// </exception>
public static IDataProtectionBuilder Use(this IDataProtectionBuilder builder, ServiceDescriptor descriptor)
{
// This algorithm of removing all other services of a specific type
// before adding the new/replacement service is how the base ASP.NET
// DataProtection bits work. Due to some of the differences in how
// that base set of bits handles DI, it's better to follow suit
// and work in the same way than to try and debug weird issues.
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
for (int i = builder.Services.Count - 1; i >= 0; i--)
{
if (builder.Services[i]?.ServiceType == descriptor.ServiceType)
{
builder.Services.RemoveAt(i);
}
}
builder.Services.Add(descriptor);
return builder;
}
}
}

+ 210
- 0
src/BuildingBlocks/DataProtection/DataProtection/RedisXmlRepository.cs View File

@ -0,0 +1,210 @@
namespace Microsoft.eShopOnContainers.BuildingBlocks
{
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;
/// <summary>
/// Key repository that stores XML encrypted keys in a Redis distributed cache.
/// </summary>
/// <remarks>
/// <para>
/// The values stored in Redis are XML documents that contain encrypted session
/// keys used for the protection of things like session state. The document contents
/// are double-encrypted - first with a changing session key; then by a master key.
/// As such, there's no risk in storing the keys in Redis - even if someone can crack
/// the master key, they still need to also crack the session key. (Other solutions
/// for sharing keys across a farm environment include writing them to files
/// on a file share.)
/// </para>
/// <para>
/// While the repository uses a hash to keep the set of encrypted keys separate, you
/// can further separate these items from other items in Redis by specifying a unique
/// database in the connection string.
/// </para>
/// <para>
/// Consumers of the repository are responsible for caching the XML items as needed.
/// Typically repositories are consumed by things like <see cref="Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider"/>
/// which generates <see cref="Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.CacheableKeyRing"/>
/// values that get cached. The mechanism is already optimized for caching so there's
/// no need to create a redundant cache.
/// </para>
/// </remarks>
/// <seealso cref="Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository" />
/// <seealso cref="System.IDisposable" />
public class RedisXmlRepository : IXmlRepository, IDisposable
{
/// <summary>
/// The root cache key for XML items stored in Redis
/// </summary>
public static readonly string RedisHashKey = "DataProtectionXmlRepository";
/// <summary>
/// The connection to the Redis backing store.
/// </summary>
private IConnectionMultiplexer _connection;
/// <summary>
/// Flag indicating whether the object has been disposed.
/// </summary>
private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="RedisXmlRepository"/> class.
/// </summary>
/// <param name="connectionString">
/// The Redis connection string.
/// </param>
/// <param name="logger">
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="connectionString" /> or <paramref name="logger" /> is <see langword="null" />.
/// </exception>
public RedisXmlRepository(string connectionString, ILogger<RedisXmlRepository> logger)
: this(ConnectionMultiplexer.Connect(connectionString), logger)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RedisXmlRepository"/> class.
/// </summary>
/// <param name="connection">
/// The Redis database connection.
/// </param>
/// <param name="logger">
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="connection" /> or <paramref name="logger" /> is <see langword="null" />.
/// </exception>
public RedisXmlRepository(IConnectionMultiplexer connection, ILogger<RedisXmlRepository> logger)
{
if (connection == null)
{
throw new ArgumentNullException(nameof(connection));
}
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
this._connection = connection;
this.Logger = logger;
// Mask the password so it doesn't get logged.
var configuration = Regex.Replace(this._connection.Configuration, @"password\s*=\s*[^,]*", "password=****", RegexOptions.IgnoreCase);
this.Logger.LogDebug("Storing data protection keys in Redis: {RedisConfiguration}", configuration);
}
/// <summary>
/// Gets the logger.
/// </summary>
/// <value>
/// The <see cref="ILogger{T}"/> used to log diagnostic messages.
/// </value>
public ILogger<RedisXmlRepository> Logger { get; private set; }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing,
/// or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
}
/// <summary>
/// Gets all top-level XML elements in the repository.
/// </summary>
/// <returns>
/// An <see cref="IReadOnlyCollection{T}"/> with the set of elements
/// stored in the repository.
/// </returns>
public IReadOnlyCollection<XElement> GetAllElements()
{
var database = this._connection.GetDatabase();
var hash = database.HashGetAll(RedisHashKey);
var elements = new List<XElement>();
if (hash == null || hash.Length == 0)
{
return elements.AsReadOnly();
}
foreach (var item in hash.ToStringDictionary())
{
elements.Add(XElement.Parse(item.Value));
}
this.Logger.LogDebug("Read {XmlElementCount} XML elements from Redis.", elements.Count);
return elements.AsReadOnly();
}
/// <summary>
/// Adds a top-level XML element to the repository.
/// </summary>
/// <param name="element">The element to add.</param>
/// <param name="friendlyName">
/// An optional name to be associated with the XML element.
/// For instance, if this repository stores XML files on disk, the friendly name may
/// be used as part of the file name. Repository implementations are not required to
/// observe this parameter even if it has been provided by the caller.
/// </param>
/// <remarks>
/// The <paramref name="friendlyName" /> parameter must be unique if specified.
/// For instance, it could be the ID of the key being stored.
/// </remarks>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="element" /> is <see langword="null" />.
/// </exception>
public void StoreElement(XElement element, string friendlyName)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
if (string.IsNullOrEmpty(friendlyName))
{
// The framework always passes in a name, but
// the contract indicates this may be null or empty.
friendlyName = Guid.NewGuid().ToString();
}
this.Logger.LogDebug("Storing XML element with friendly name {XmlElementFriendlyName}.", friendlyName);
this._connection.GetDatabase().HashSet(RedisHashKey, friendlyName, element.ToString());
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
if (this._connection != null)
{
this._connection.Close();
this._connection.Dispose();
}
}
this._connection = null;
this._disposed = true;
}
}
}
}

+ 8
- 0
src/BuildingBlocks/EventBus/CommandBus/CommandBus.csproj View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.0</TargetFramework>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.CommandBus</RootNamespace>
</PropertyGroup>
</Project>

+ 16
- 0
src/BuildingBlocks/EventBus/CommandBus/ICommandBus.cs View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
{
public interface ICommandBus
{
void Send<T>(string name, T data);
void Handle<TC>(string name, IIntegrationCommandHandler<TC> handler);
void Handle(string name, IIntegrationCommandHandler handler);
void Handle<TI, TC>(TI handler)
where TI : IIntegrationCommandHandler<TC>;
}
}

+ 16
- 0
src/BuildingBlocks/EventBus/CommandBus/IIntegrationCommandHandler.cs View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
{
public interface IIntegrationCommandHandler
{
void Handle(IntegrationCommand command);
}
public interface IIntegrationCommandHandler<T> : IIntegrationCommandHandler
{
void Handle(IntegrationCommand<T> command);
}
}

+ 35
- 0
src/BuildingBlocks/EventBus/CommandBus/IntegrationCommand.cs View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.CommandBus
{
public abstract class IntegrationCommand
{
public Guid Id { get; }
public DateTime Sent { get; }
public abstract object GetDataAsObject();
protected IntegrationCommand()
{
Id = Guid.NewGuid();
Sent = DateTime.UtcNow;
}
}
public class IntegrationCommand<T> : IntegrationCommand
{
public T Data { get; }
public string Name { get; }
public override object GetDataAsObject() => Data;
public IntegrationCommand(string name, T data) : base()
{
Data = data;
Name = name;
}
}
}

+ 5
- 5
src/BuildingBlocks/EventBus/EventBus.Tests/InMemory_SubscriptionManager_Tests.cs View File

@ -18,7 +18,7 @@ namespace EventBus.Tests
public void After_One_Event_Subscription_Should_Contain_The_Event()
{
var manager = new InMemoryEventBusSubscriptionsManager();
manager.AddSubscription<TestIntegrationEvent,TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
manager.AddSubscription<TestIntegrationEvent,TestIntegrationEventHandler>();
Assert.True(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
}
@ -26,7 +26,7 @@ namespace EventBus.Tests
public void After_All_Subscriptions_Are_Deleted_Event_Should_No_Longer_Exists()
{
var manager = new InMemoryEventBusSubscriptionsManager();
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
manager.RemoveSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
Assert.False(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
}
@ -37,7 +37,7 @@ namespace EventBus.Tests
bool raised = false;
var manager = new InMemoryEventBusSubscriptionsManager();
manager.OnEventRemoved += (o, e) => raised = true;
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
manager.RemoveSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
Assert.True(raised);
}
@ -46,8 +46,8 @@ namespace EventBus.Tests
public void Get_Handlers_For_Event_Should_Return_All_Handlers()
{
var manager = new InMemoryEventBusSubscriptionsManager();
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
manager.AddSubscription<TestIntegrationEvent, TestIntegrationOtherEventHandler>(() => new TestIntegrationOtherEventHandler());
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
manager.AddSubscription<TestIntegrationEvent, TestIntegrationOtherEventHandler>();
var handlers = manager.GetHandlersForEvent<TestIntegrationEvent>();
Assert.Equal(2, handlers.Count());
}


+ 13
- 0
src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs View File

@ -0,0 +1,13 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
{
public interface IDynamicIntegrationEventHandler
{
Task Handle(dynamic eventData);
}
}

+ 7
- 1
src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs View File

@ -5,9 +5,15 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
{
public interface IEventBus
{
void Subscribe<T, TH>(Func<TH> handler)
void Subscribe<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>;
void SubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler;
void UnsubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler;
void Unsubscribe<T, TH>()
where TH : IIntegrationEventHandler<T>
where T : IntegrationEvent;


+ 1
- 2
src/BuildingBlocks/EventBus/EventBus/EventBus.csproj View File

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.EventBus</RootNamespace>
</PropertyGroup>
@ -11,7 +10,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
</ItemGroup>
</Project>

+ 14
- 6
src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs View File

@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using static Microsoft.eShopOnContainers.BuildingBlocks.EventBus.InMemoryEventBusSubscriptionsManager;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
{
@ -9,18 +10,25 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
{
bool IsEmpty { get; }
event EventHandler<string> OnEventRemoved;
void AddSubscription<T, TH>(Func<TH> handler)
void AddDynamicSubscription<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler;
void AddSubscription<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>;
void RemoveSubscription<T, TH>()
where TH : IIntegrationEventHandler<T>
where T : IntegrationEvent;
void RemoveSubscription<T, TH>()
where TH : IIntegrationEventHandler<T>
where T : IntegrationEvent;
void RemoveDynamicSubscription<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler;
bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent;
bool HasSubscriptionsForEvent(string eventName);
Type GetEventTypeByName(string eventName);
void Clear();
IEnumerable<Delegate> GetHandlersForEvent<T>() where T : IntegrationEvent;
IEnumerable<Delegate> GetHandlersForEvent(string eventName);
IEnumerable<SubscriptionInfo> GetHandlersForEvent<T>() where T : IntegrationEvent;
IEnumerable<SubscriptionInfo> GetHandlersForEvent(string eventName);
string GetEventKey<T>();
}
}

+ 84
- 37
src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs View File

@ -8,64 +8,106 @@ using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
{
public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
{
private readonly Dictionary<string, List<Delegate>> _handlers;
private readonly Dictionary<string, List<SubscriptionInfo>> _handlers;
private readonly List<Type> _eventTypes;
public event EventHandler<string> OnEventRemoved;
public InMemoryEventBusSubscriptionsManager()
{
_handlers = new Dictionary<string, List<Delegate>>();
_handlers = new Dictionary<string, List<SubscriptionInfo>>();
_eventTypes = new List<Type>();
}
public bool IsEmpty => !_handlers.Keys.Any();
public void Clear() => _handlers.Clear();
public void AddSubscription<T, TH>(Func<TH> handler)
public void AddDynamicSubscription<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
DoAddSubscription(typeof(TH), eventName, isDynamic: true);
}
public void AddSubscription<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>
{
var key = GetEventKey<T>();
if (!HasSubscriptionsForEvent<T>())
var eventName = GetEventKey<T>();
DoAddSubscription(typeof(TH), eventName, isDynamic: false);
_eventTypes.Add(typeof(T));
}
private void DoAddSubscription(Type handlerType, string eventName, bool isDynamic)
{
if (!HasSubscriptionsForEvent(eventName))
{
_handlers.Add(key, new List<Delegate>());
_handlers.Add(eventName, new List<SubscriptionInfo>());
}
_handlers[key].Add(handler);
_eventTypes.Add(typeof(T));
if (_handlers[eventName].Any(s => s.HandlerType == handlerType))
{
throw new ArgumentException(
$"Handler Type {handlerType.Name} already registered for '{eventName}'", nameof(handlerType));
}
if (isDynamic)
{
_handlers[eventName].Add(SubscriptionInfo.Dynamic(handlerType));
}
else
{
_handlers[eventName].Add(SubscriptionInfo.Typed(handlerType));
}
}
public void RemoveDynamicSubscription<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
var handlerToRemove = FindDynamicSubscriptionToRemove<TH>(eventName);
DoRemoveHandler(eventName, handlerToRemove);
}
public void RemoveSubscription<T, TH>()
where TH : IIntegrationEventHandler<T>
where T : IntegrationEvent
{
var handlerToRemove = FindHandlerToRemove<T, TH>();
if (handlerToRemove != null)
var handlerToRemove = FindSubscriptionToRemove<T, TH>();
var eventName = GetEventKey<T>();
DoRemoveHandler(eventName, handlerToRemove);
}
private void DoRemoveHandler(string eventName, SubscriptionInfo subsToRemove)
{
if (subsToRemove != null)
{
var key = GetEventKey<T>();
_handlers[key].Remove(handlerToRemove);
if (!_handlers[key].Any())
_handlers[eventName].Remove(subsToRemove);
if (!_handlers[eventName].Any())
{
_handlers.Remove(key);
var eventType = _eventTypes.SingleOrDefault(e => e.Name == key);
_handlers.Remove(eventName);
var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName);
if (eventType != null)
{
_eventTypes.Remove(eventType);
RaiseOnEventRemoved(eventType.Name);
}
RaiseOnEventRemoved(eventName);
}
}
}
public IEnumerable<Delegate> GetHandlersForEvent<T>() where T : IntegrationEvent
public IEnumerable<SubscriptionInfo> GetHandlersForEvent<T>() where T : IntegrationEvent
{
var key = GetEventKey<T>();
return GetHandlersForEvent(key);
}
public IEnumerable<Delegate> GetHandlersForEvent(string eventName) => _handlers[eventName];
public IEnumerable<SubscriptionInfo> GetHandlersForEvent(string eventName) => _handlers[eventName];
private void RaiseOnEventRemoved(string eventName)
{
@ -76,26 +118,31 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
}
}
private Delegate FindHandlerToRemove<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>
private SubscriptionInfo FindDynamicSubscriptionToRemove<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
return DoFindSubscriptionToRemove(eventName, typeof(TH));
}
private SubscriptionInfo FindSubscriptionToRemove<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>
{
var eventName = GetEventKey<T>();
return DoFindSubscriptionToRemove(eventName, typeof(TH));
}
private SubscriptionInfo DoFindSubscriptionToRemove(string eventName, Type handlerType)
{
if (!HasSubscriptionsForEvent<T>())
if (!HasSubscriptionsForEvent(eventName))
{
return null;
}
var key = GetEventKey<T>();
foreach (var func in _handlers[key])
{
var genericArgs = func.GetType().GetGenericArguments();
if (genericArgs.SingleOrDefault() == typeof(TH))
{
return func;
}
}
return _handlers[eventName].SingleOrDefault(s => s.HandlerType == handlerType);
return null;
}
public bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent
@ -104,10 +151,10 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
return HasSubscriptionsForEvent(key);
}
public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName);
public Type GetEventTypeByName(string eventName) => _eventTypes.Single(t => t.Name == eventName);
private string GetEventKey<T>()
public Type GetEventTypeByName(string eventName) => _eventTypes.SingleOrDefault(t => t.Name == eventName);
public string GetEventKey<T>()
{
return typeof(T).Name;
}


+ 28
- 0
src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs View File

@ -0,0 +1,28 @@
using System;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
{
public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
{
public class SubscriptionInfo
{
public bool IsDynamic { get; }
public Type HandlerType{ get; }
private SubscriptionInfo(bool isDynamic, Type handlerType)
{
IsDynamic = isDynamic;
HandlerType = handlerType;
}
public static SubscriptionInfo Dynamic(Type handlerType)
{
return new SubscriptionInfo(true, handlerType);
}
public static SubscriptionInfo Typed(Type handlerType)
{
return new SubscriptionInfo(false, handlerType);
}
}
}
}

+ 145
- 0
src/BuildingBlocks/EventBus/EventBusRabbitMQ/CommandBusRabbitMQ.cs View File

@ -0,0 +1,145 @@
//using Microsoft.eShopOnContainers.BuildingBlocks.CommandBus;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Polly;
using Polly.Retry;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
/*
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
{
public class CommandBusRabbitMQ : ICommandBus, IDisposable
{
const string BROKER_NAME = "eshop_command_bus";
private readonly IRabbitMQPersistentConnection _persistentConnection;
private readonly ILogger<CommandBusRabbitMQ> _logger;
private IModel _consumerChannel;
private string _queueName;
private readonly Dictionary<string, IIntegrationCommandHandler> _handlers;
private readonly Dictionary<string, Type> _typeMappings;
public CommandBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection,
ILogger<CommandBusRabbitMQ> logger)
{
_logger = logger;
_persistentConnection = persistentConnection;
_handlers = new Dictionary<string, IIntegrationCommandHandler>();
_typeMappings = new Dictionary<string, Type>();
}
public void Send<T>(string name, T data)
{
Send(new IntegrationCommand<T>(name, data));
}
public void Handle<TC>(string name, IIntegrationCommandHandler<TC> handler)
{
_handlers.Add(name, handler);
_typeMappings.Add(name, typeof(TC));
}
public void Handle(string name, IIntegrationCommandHandler handler)
{
_handlers.Add(name, handler);
}
public void Handle<TI, TC>(TI handler) where TI : IIntegrationCommandHandler<TC>
{
var name = typeof(TI).Name;
_handlers.Add(name, handler);
_typeMappings.Add(name, typeof(TC));
}
private void Send<T>(IntegrationCommand<T> command)
{
if (!_persistentConnection.IsConnected)
{
_persistentConnection.TryConnect();
}
var policy = RetryPolicy.Handle<BrokerUnreachableException>()
.Or<SocketException>()
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
{
_logger.LogWarning(ex.ToString());
});
using (var channel = _persistentConnection.CreateModel())
{
var commandName = command.Name;
channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct");
var message = JsonConvert.SerializeObject(command);
var body = Encoding.UTF8.GetBytes(message);
policy.Execute(() =>
{
channel.BasicPublish(exchange: BROKER_NAME,
routingKey: commandName,
basicProperties: null,
body: body);
});
}
}
private IModel CreateConsumerChannel()
{
if (!_persistentConnection.IsConnected)
{
_persistentConnection.TryConnect();
}
var channel = _persistentConnection.CreateModel();
channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct");
_queueName = channel.QueueDeclare().QueueName;
var consumer = new EventingBasicConsumer(channel);
consumer.Received += async (model, ea) =>
{
var commandName = ea.RoutingKey;
var message = Encoding.UTF8.GetString(ea.Body);
await InvokeHandler(commandName, message);
};
channel.BasicConsume(queue: _queueName,
noAck: true,
consumer: consumer);
channel.CallbackException += (sender, ea) =>
{
_consumerChannel.Dispose();
_consumerChannel = CreateConsumerChannel();
};
return channel;
}
private Task InvokeHandler(string commandName, string message)
{
if (_handlers.ContainsKey(commandName))
{
}
}
public void Dispose()
{
if (_consumerChannel != null)
{
_consumerChannel.Dispose();
}
}
}
}
*/

+ 49
- 28
src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs View File

@ -1,8 +1,10 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Autofac;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Polly;
using Polly.Retry;
using RabbitMQ.Client;
@ -25,17 +27,20 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
private readonly IRabbitMQPersistentConnection _persistentConnection;
private readonly ILogger<EventBusRabbitMQ> _logger;
private readonly IEventBusSubscriptionsManager _subsManager;
private readonly ILifetimeScope _autofac;
private readonly string AUTOFAC_SCOPE_NAME = "eshop_event_bus";
private IModel _consumerChannel;
private string _queueName;
public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger, IEventBusSubscriptionsManager subsManager)
public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger,
ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager)
{
_persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager();
_consumerChannel = CreateConsumerChannel();
_autofac = autofac;
_subsManager.OnEventRemoved += SubsManager_OnEventRemoved;
}
@ -96,12 +101,25 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
}
}
public void Subscribe<T, TH>(Func<TH> handler)
public void SubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
DoInternalSubscription(eventName);
_subsManager.AddDynamicSubscription<TH>(eventName);
}
public void Subscribe<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>
{
var eventName = typeof(T).Name;
var containsKey = _subsManager.HasSubscriptionsForEvent<T>();
var eventName = _subsManager.GetEventKey<T>();
DoInternalSubscription(eventName);
_subsManager.AddSubscription<T, TH>();
}
private void DoInternalSubscription(string eventName)
{
var containsKey = _subsManager.HasSubscriptionsForEvent(eventName);
if (!containsKey)
{
if (!_persistentConnection.IsConnected)
@ -116,9 +134,6 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
routingKey: eventName);
}
}
_subsManager.AddSubscription<T, TH>(handler);
}
public void Unsubscribe<T, TH>()
@ -128,19 +143,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
_subsManager.RemoveSubscription<T, TH>();
}
private static Func<IIntegrationEventHandler> FindHandlerByType(Type handlerType, IEnumerable<Func<IIntegrationEventHandler>> handlers)
public void UnsubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
foreach (var func in handlers)
{
if (func.GetMethodInfo().ReturnType == handlerType)
{
return func;
}
}
return null;
_subsManager.RemoveDynamicSubscription<TH>(eventName);
}
public void Dispose()
{
if (_consumerChannel != null)
@ -190,17 +199,29 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
private async Task ProcessEvent(string eventName, string message)
{
if (_subsManager.HasSubscriptionsForEvent(eventName))
{
var eventType = _subsManager.GetEventTypeByName(eventName);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
var handlers = _subsManager.GetHandlersForEvent(eventName);
foreach (var handlerfactory in handlers)
if (_subsManager.HasSubscriptionsForEvent(eventName))
{
using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME))
{
var handler = handlerfactory.DynamicInvoke();
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
var subscriptions = _subsManager.GetHandlersForEvent(eventName);
foreach (var subscription in subscriptions)
{
if (subscription.IsDynamic)
{
var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler;
dynamic eventData = JObject.Parse(message);
await handler.Handle(eventData);
}
else
{
var eventType = _subsManager.GetEventTypeByName(eventName);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
var handler = scope.ResolveOptional(subscription.HandlerType);
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
}
}
}
}
}


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

@ -2,16 +2,16 @@
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Polly" Version="5.0.6" />
<PackageReference Include="RabbitMQ.Client" Version="4.1.1" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
<PackageReference Include="Autofac" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="Polly" Version="5.1.0" />
<PackageReference Include="RabbitMQ.Client" Version="4.1.3" />
<PackageReference Include="System.ValueTuple" Version="4.3.1" />
</ItemGroup>
<ItemGroup>


+ 7
- 8
src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj View File

@ -2,18 +2,17 @@
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
</ItemGroup>
<ItemGroup>


+ 2
- 2
src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/Microsoft.AspNetCore.HealthChecks.csproj View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
</PropertyGroup>
<ItemGroup>
@ -9,7 +9,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="1.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="1.1.2" />
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>

+ 1
- 1
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/Microsoft.Extensions.HealthChecks.SqlServer.csproj View File

@ -9,7 +9,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Data.SqlClient" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.3.1" />
</ItemGroup>
<ItemGroup>


+ 2
- 2
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Microsoft.Extensions.HealthChecks.csproj View File

@ -9,8 +9,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />


+ 3
- 3
src/BuildingBlocks/Resilience/Resilience.Http/Resilience.Http.csproj View File

@ -6,9 +6,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Polly" Version="5.0.6" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="Polly" Version="5.1.0" />
</ItemGroup>
</Project>

+ 5
- 5
src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs View File

@ -96,7 +96,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
// as it is disposed after each call
var origin = GetOriginFromUri(uri);
return HttpInvoker(origin, () =>
return HttpInvoker(origin, async () =>
{
var requestMessage = new HttpRequestMessage(method, uri);
@ -112,7 +112,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
requestMessage.Headers.Add("x-requestid", requestId);
}
var response = _client.SendAsync(requestMessage).Result;
var response = await _client.SendAsync(requestMessage);
// raise exception if HttpResponseCode 500
// needed for circuit breaker to track fails
@ -122,7 +122,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
throw new HttpRequestException();
}
return Task.FromResult(response);
return response;
});
}
@ -132,13 +132,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
if (!_policyWrappers.TryGetValue(normalizedOrigin, out PolicyWrap policyWrap))
{
policyWrap = Policy.Wrap(_policyCreator(normalizedOrigin).ToArray());
policyWrap = Policy.WrapAsync(_policyCreator(normalizedOrigin).ToArray());
_policyWrappers.TryAdd(normalizedOrigin, policyWrap);
}
// Executes the action applying all
// the policies defined in the wrapper
return await policyWrap.Execute(action, new Context(normalizedOrigin));
return await policyWrap.ExecuteAsync(action, new Context(normalizedOrigin));
}


+ 6
- 6
src/Mobile/README.md View File

@ -1,12 +1,12 @@
#eShopOnContainers
# eShopOnContainers
eShopOnContainers is a reference app whose imagined purpose is to serve the mobile workforce of a fictitious company that sells products. The app allow to manage the catalog, view products, manage the basket and the orders.
<img src="Images/eShopOnContainers_Architecture_Diagram.png" alt="eShopOnContainers" Width="800" />
###Supported platforms: iOS, Android and Windows
### Supported platforms: iOS, Android and Windows
###The app architecture consists of two parts:
### The app architecture consists of two parts:
1. A Xamarin.Forms mobile app for iOS, Android and Windows.
2. Several .NET Web API microservices deployed as Docker containers.
@ -34,7 +34,7 @@ This project exercises the following platforms, frameworks or features:
* Entity Framework
* Identity Server 4
##Three platforms
## Three platforms
The app targets **three** platforms:
* iOS
@ -45,7 +45,7 @@ The app targets **three** platforms:
As of 07/03/2017, eShopOnContainers features **89.2% code share** (7.2% iOS / 16.7% Android / 8.7% Windows).
##Licenses
## Licenses
This project uses some third-party assets with a license that requires attribution:
@ -155,4 +155,4 @@ In the configuration window of the machine, go to the Compatibility section and
<img src="Images/set-compatibility-vs-sml.png" alt="Migrate to a physical computer with a different processor version" Width="600" />
## Copyright and license
* Code and documentation copyright 2017 Microsoft Corp. Code released under the [MIT license](https://opensource.org/licenses/MIT).
* Code and documentation copyright 2017 Microsoft Corp. Code released under the [MIT license](https://opensource.org/licenses/MIT).

+ 41
- 4
src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs View File

@ -1,7 +1,11 @@
using eShopOnContainers.Core.Helpers;
using System;
using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Services;
using eShopOnContainers.Core.ViewModels.Base;
using System.Threading.Tasks;
using eShopOnContainers.Core.Models.Location;
using eShopOnContainers.Core.Services.Location;
using Plugin.Geolocator;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@ -36,6 +40,7 @@ namespace eShopOnContainers
return navigationService.InitializeAsync();
}
protected override async void OnStart()
{
base.OnStart();
@ -44,6 +49,18 @@ namespace eShopOnContainers
{
await InitNavigation();
}
if (!Settings.UseFakeLocation)
{
await GetRealLocation();
}
if (!Settings.UseMocks && !string.IsNullOrEmpty(Settings.UserId))
{
await SendCurrentLocation();
}
base.OnResume();
}
protected override void OnSleep()
@ -51,9 +68,29 @@ namespace eShopOnContainers
// Handle when your app sleeps
}
protected override void OnResume()
private async Task GetRealLocation()
{
// Handle when your app resumes
var locator = CrossGeolocator.Current;
locator.AllowsBackgroundUpdates = true;
locator.DesiredAccuracy = 50;
var position = await locator.GetPositionAsync(20000);
Settings.Latitude = position.Latitude;
Settings.Longitude = position.Longitude;
}
private async Task SendCurrentLocation()
{
var location = new Location
{
Latitude = Settings.Latitude,
Longitude = Settings.Longitude
};
var locationService = ViewModelLocator.Resolve<ILocationService>();
await locationService.UpdateUserLocation(location,
Settings.AuthAccessToken);
}
}
}
}

+ 22
- 10
src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs View File

@ -6,7 +6,6 @@
public const string MockTag = "Mock";
public const string DefaultEndpoint = "http://13.88.8.119";
private string _baseEndpoint;
private static readonly GlobalSetting _instance = new GlobalSetting();
@ -31,6 +30,10 @@
}
}
public string ClientId { get { return "xamarin"; }}
public string ClientSecret { get { return "secret"; }}
public string AuthToken { get; set; }
public string RegisterWebsite { get; set; }
@ -43,8 +46,14 @@
public string IdentityEndpoint { get; set; }
public string LocationEndpoint { get; set; }
public string MarketingEndpoint { get; set; }
public string UserInfoEndpoint { get; set; }
public string TokenEndpoint { get; set; }
public string LogoutEndpoint { get; set; }
public string IdentityCallback { get; set; }
@ -53,15 +62,18 @@
private void UpdateEndpoint(string baseEndpoint)
{
RegisterWebsite = string.Format("{0}:5105/Account/Register", baseEndpoint);
CatalogEndpoint = string.Format("{0}:5101", baseEndpoint);
OrdersEndpoint = string.Format("{0}:5102", baseEndpoint);
BasketEndpoint = string.Format("{0}:5103", baseEndpoint);
IdentityEndpoint = string.Format("{0}:5105/connect/authorize", baseEndpoint);
UserInfoEndpoint = string.Format("{0}:5105/connect/userinfo", baseEndpoint);
LogoutEndpoint = string.Format("{0}:5105/connect/endsession", baseEndpoint);
IdentityCallback = string.Format("{0}:5105/xamarincallback", baseEndpoint);
LogoutCallback = string.Format("{0}:5105/Account/Redirecting", baseEndpoint);
RegisterWebsite = $"{baseEndpoint}:5105/Account/Register";
CatalogEndpoint = $"{baseEndpoint}:5101";
OrdersEndpoint = $"{baseEndpoint}:5102";
BasketEndpoint = $"{baseEndpoint}:5103";
IdentityEndpoint = $"{baseEndpoint}:5105/connect/authorize";
UserInfoEndpoint = $"{baseEndpoint}:5105/connect/userinfo";
TokenEndpoint = $"{baseEndpoint}:5105/connect/token";
LogoutEndpoint = $"{baseEndpoint}:5105/connect/endsession";
IdentityCallback = $"{baseEndpoint}:5105/xamarincallback";
LogoutCallback = $"{baseEndpoint}:5105/Account/Redirecting";
LocationEndpoint = $"{baseEndpoint}:5109";
MarketingEndpoint = $"{baseEndpoint}:5110";
}
}
}

+ 34
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/ServicesHelper.cs View File

@ -1,5 +1,6 @@
using eShopOnContainers.Core.Models.Basket;
using eShopOnContainers.Core.Models.Catalog;
using eShopOnContainers.Core.Models.Marketing;
using eShopOnContainers.Core.ViewModels.Base;
using System;
using System.Collections.Generic;
@ -76,5 +77,38 @@ namespace eShopOnContainers.Core.Helpers
Debug.WriteLine(ex.Message);
}
}
public static void FixCatalogItemPictureUri(IEnumerable<CampaignItem> campaignItems)
{
if (campaignItems == null)
{
return;
}
try
{
if (!ViewModelLocator.UseMockService
&& Settings.UrlBase != GlobalSetting.DefaultEndpoint)
{
foreach (var catalogItem in campaignItems)
{
MatchCollection serverResult = IpRegex.Matches(catalogItem.PictureUri);
MatchCollection localResult = IpRegex.Matches(Settings.UrlBase);
if (serverResult.Count != -1 && localResult.Count != -1)
{
var serviceIp = serverResult[0].Value;
var localIp = localResult[0].Value;
catalogItem.PictureUri = catalogItem.PictureUri.Replace(serviceIp, localIp);
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}

+ 38
- 32
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs View File

@ -20,63 +20,69 @@ namespace eShopOnContainers.Core.Helpers
#region Setting Constants
private const string IdUserId = "user_id";
private const string AccessToken = "access_token";
private const string IdToken = "id_token";
private const string IdUseMocks = "use_mocks";
private const string IdUrlBase = "url_base";
private const string IdUseFakeLocation = "use_fake_location";
private const string IdLatitude = "latitude";
private const string IdLongitude = "flongitude";
private static readonly string AccessTokenDefault = string.Empty;
private static readonly string IdTokenDefault = string.Empty;
private static readonly bool UseMocksDefault = true;
private static readonly bool UseFakeLocationDefault = false;
private static readonly double FakeLatitudeValue = 47.604610d;
private static readonly double FakeLongitudeValue = -122.315752d;
private static readonly string UrlBaseDefault = GlobalSetting.Instance.BaseEndpoint;
#endregion
public static string UserId
{
get => AppSettings.GetValueOrDefault<string>(IdUserId);
set => AppSettings.AddOrUpdateValue<string>(IdUserId, value);
}
public static string AuthAccessToken
{
get
{
return AppSettings.GetValueOrDefault<string>(AccessToken, AccessTokenDefault);
}
set
{
AppSettings.AddOrUpdateValue<string>(AccessToken, value);
}
get => AppSettings.GetValueOrDefault<string>(AccessToken, AccessTokenDefault);
set => AppSettings.AddOrUpdateValue<string>(AccessToken, value);
}
public static string AuthIdToken
{
get
{
return AppSettings.GetValueOrDefault<string>(IdToken, IdTokenDefault);
}
set
{
AppSettings.AddOrUpdateValue<string>(IdToken, value);
}
get => AppSettings.GetValueOrDefault<string>(IdToken, IdTokenDefault);
set => AppSettings.AddOrUpdateValue<string>(IdToken, value);
}
public static bool UseMocks
{
get
{
return AppSettings.GetValueOrDefault<bool>(IdUseMocks, UseMocksDefault);
}
set
{
AppSettings.AddOrUpdateValue<bool>(IdUseMocks, value);
}
get => AppSettings.GetValueOrDefault<bool>(IdUseMocks, UseMocksDefault);
set => AppSettings.AddOrUpdateValue<bool>(IdUseMocks, value);
}
public static string UrlBase
{
get
{
return AppSettings.GetValueOrDefault<string>(IdUrlBase, UrlBaseDefault);
}
set
{
AppSettings.AddOrUpdateValue<string>(IdUrlBase, value);
}
get => AppSettings.GetValueOrDefault<string>(IdUrlBase, UrlBaseDefault);
set => AppSettings.AddOrUpdateValue<string>(IdUrlBase, value);
}
public static bool UseFakeLocation
{
get => AppSettings.GetValueOrDefault<bool>(IdUseFakeLocation, UseFakeLocationDefault);
set => AppSettings.AddOrUpdateValue<bool>(IdUseFakeLocation, value);
}
public static double Latitude
{
get => AppSettings.GetValueOrDefault<double>(IdLatitude, FakeLatitudeValue);
set => AppSettings.AddOrUpdateValue<double>(IdLatitude, value);
}
public static double Longitude
{
get => AppSettings.GetValueOrDefault<double>(IdLongitude, FakeLongitudeValue);
set => AppSettings.AddOrUpdateValue<double>(IdLongitude, value);
}
}
}

+ 36
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Basket/BasketCheckout.cs View File

@ -0,0 +1,36 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace eShopOnContainers.Core.Models.Basket
{
public class BasketCheckout
{
[Required]
public string City { get; set; }
[Required]
public string Street { get; set; }
[Required]
public string State { get; set; }
[Required]
public string Country { get; set; }
public string ZipCode { get; set; }
[Required]
public string CardNumber { get; set; }
[Required]
public string CardHolderName { get; set; }
[Required]
public DateTime CardExpiration { get; set; }
[Required]
public string CardSecurityNumber { get; set; }
public int CardTypeId { get; set; }
public string Buyer { get; set; }
[Required]
public Guid RequestId { get; set; }
}
}

+ 8
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Location/Location.cs View File

@ -0,0 +1,8 @@
namespace eShopOnContainers.Core.Models.Location
{
public class Location
{
public double Longitude { get; set; }
public double Latitude { get; set; }
}
}

+ 19
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Marketing/Campaign.cs View File

@ -0,0 +1,19 @@
namespace eShopOnContainers.Core.Models.Marketing
{
using System;
public class Campaign
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
public string PictureUri { get; set; }
}
}

+ 19
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Marketing/CampaignItem.cs View File

@ -0,0 +1,19 @@
namespace eShopOnContainers.Core.Models.Marketing
{
using System;
public class CampaignItem
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
public string PictureUri { get; set; }
}
}

+ 12
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Marketing/CampaignRoot.cs View File

@ -0,0 +1,12 @@
namespace eShopOnContainers.Core.Models.Marketing
{
using System.Collections.Generic;
public class CampaignRoot
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public int Count { get; set; }
public List<CampaignItem> Data { get; set; }
}
}

+ 22
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Token/UserToken.cs View File

@ -0,0 +1,22 @@
using Newtonsoft.Json;
namespace eShopOnContainers.Core.Models.Token
{
public class UserToken
{
[JsonProperty("id_token")]
public string IdToken { get; set; }
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}
}

+ 6
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketMockService.cs View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
using System;
namespace eShopOnContainers.Core.Services.Basket
{
@ -57,5 +58,10 @@ namespace eShopOnContainers.Core.Services.Basket
MockCustomBasket.Items.Clear();
}
}
public Task CheckoutAsync(BasketCheckout basketCheckout, string token)
{
throw new NotImplementedException();
}
}
}

+ 9
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketService.cs View File

@ -42,6 +42,15 @@ namespace eShopOnContainers.Core.Services.Basket
return result;
}
public async Task CheckoutAsync(BasketCheckout basketCheckout, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint + "/checkout");
string uri = builder.ToString();
await _requestProvider.PostAsync(uri, basketCheckout, token);
}
public async Task ClearBasketAsync(string guidUser, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);


+ 1
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/IBasketService.cs View File

@ -7,6 +7,7 @@ namespace eShopOnContainers.Core.Services.Basket
{
Task<CustomerBasket> GetBasketAsync(string guidUser, string token);
Task<CustomerBasket> UpdateBasketAsync(CustomerBasket customerBasket, string token);
Task CheckoutAsync(BasketCheckout basketCheckout, string token);
Task ClearBasketAsync(string guidUser, string token);
}
}

+ 3
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Common/Common.cs View File

@ -7,5 +7,8 @@
public static string MockCatalogItemId03 = "3";
public static string MockCatalogItemId04 = "4";
public static string MockCatalogItemId05 = "5";
public static int MockCampaignd01 = 1;
public static int MockCampaignd02 = 2;
}
}

+ 5
- 1
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Identity/IIdentityService.cs View File

@ -1,8 +1,12 @@
namespace eShopOnContainers.Core.Services.Identity
using eShopOnContainers.Core.Models.Token;
using System.Threading.Tasks;
namespace eShopOnContainers.Core.Services.Identity
{
public interface IIdentityService
{
string CreateAuthorizationRequest();
string CreateLogoutRequest(string token);
Task<UserToken> GetTokenAsync(string code);
}
}

+ 24
- 6
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Identity/IdentityService.cs View File

@ -1,11 +1,22 @@
using IdentityModel.Client;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using eShopOnContainers.Core.Services.RequestProvider;
using eShopOnContainers.Core.Models.Token;
namespace eShopOnContainers.Core.Services.Identity
{
public class IdentityService : IIdentityService
{
private readonly IRequestProvider _requestProvider;
public IdentityService(IRequestProvider requestProvider)
{
_requestProvider = requestProvider;
}
public string CreateAuthorizationRequest()
{
// Create URI to authorization endpoint
@ -13,10 +24,10 @@ namespace eShopOnContainers.Core.Services.Identity
// Dictionary with values for the authorize request
var dic = new Dictionary<string, string>();
dic.Add("client_id", "xamarin");
dic.Add("response_type", "id_token token");
dic.Add("scope", "openid profile basket orders");
dic.Add("client_id", GlobalSetting.Instance.ClientId);
dic.Add("client_secret", GlobalSetting.Instance.ClientSecret);
dic.Add("response_type", "code id_token");
dic.Add("scope", "openid profile basket orders locations marketing offline_access");
dic.Add("redirect_uri", GlobalSetting.Instance.IdentityCallback);
dic.Add("nonce", Guid.NewGuid().ToString("N"));
@ -24,13 +35,13 @@ namespace eShopOnContainers.Core.Services.Identity
var currentCSRFToken = Guid.NewGuid().ToString("N");
dic.Add("state", currentCSRFToken);
var authorizeUri = authorizeRequest.Create(dic);
var authorizeUri = authorizeRequest.Create(dic);
return authorizeUri;
}
public string CreateLogoutRequest(string token)
{
if(string.IsNullOrEmpty(token))
if (string.IsNullOrEmpty(token))
{
return string.Empty;
}
@ -40,5 +51,12 @@ namespace eShopOnContainers.Core.Services.Identity
token,
GlobalSetting.Instance.LogoutCallback);
}
public async Task<UserToken> GetTokenAsync(string code)
{
string data = string.Format("grant_type=authorization_code&code={0}&redirect_uri={1}", code, WebUtility.UrlEncode(GlobalSetting.Instance.IdentityCallback));
var token = await _requestProvider.PostAsync<UserToken>(GlobalSetting.Instance.TokenEndpoint, data, GlobalSetting.Instance.ClientId, GlobalSetting.Instance.ClientSecret);
return token;
}
}
}

+ 10
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/ILocationService.cs View File

@ -0,0 +1,10 @@
namespace eShopOnContainers.Core.Services.Location
{
using System.Threading.Tasks;
using eShopOnContainers.Core.Models.Location;
public interface ILocationService
{
Task UpdateUserLocation(Location newLocReq, string token);
}
}

+ 28
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs View File

@ -0,0 +1,28 @@
namespace eShopOnContainers.Core.Services.Location
{
using eShopOnContainers.Core.Models.Location;
using eShopOnContainers.Core.Services.RequestProvider;
using System;
using System.Threading.Tasks;
public class LocationService : ILocationService
{
private readonly IRequestProvider _requestProvider;
public LocationService(IRequestProvider requestProvider)
{
_requestProvider = requestProvider;
}
public async Task UpdateUserLocation(Location newLocReq, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.LocationEndpoint);
builder.Path = "api/v1/locations";
string uri = builder.ToString();
await _requestProvider.PostAsync(uri, newLocReq, token);
}
}
}

+ 53
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignMockService.cs View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.Linq;
namespace eShopOnContainers.Core.Services.Marketing
{
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Models.Marketing;
using Xamarin.Forms;
public class CampaignMockService : ICampaignService
{
private readonly ObservableCollection<CampaignItem> _mockCampaign = new ObservableCollection<CampaignItem>
{
new CampaignItem
{
Id = Common.Common.MockCampaignd01,
PictureUri = Device.RuntimePlatform != Device.Windows
? "fake_campaign_01.png"
: "Assets/fake_campaign_01.png",
Name = ".NET Bot Black Hoodie 50% OFF",
Description = "Campaign Description 1",
From = DateTime.Now,
To = DateTime.Now.AddDays(7)
},
new CampaignItem
{
Id = Common.Common.MockCampaignd02,
PictureUri = Device.RuntimePlatform != Device.Windows
? "fake_campaign_02.png"
: "Assets/fake_campaign_02.png",
Name = "Roslyn Red T-Shirt 3x2",
Description = "Campaign Description 2",
From = DateTime.Now.AddDays(-7),
To = DateTime.Now.AddDays(14)
}
};
public async Task<ObservableCollection<CampaignItem>> GetAllCampaignsAsync(string userId, string token)
{
await Task.Delay(500);
return _mockCampaign;
}
public async Task<CampaignItem> GetCampaignByIdAsync(int campaignId, string token)
{
return _mockCampaign.SingleOrDefault(c => c.Id == campaignId);
}
}
}

+ 53
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignService.cs View File

@ -0,0 +1,53 @@
using eShopOnContainers.Core.Extensions;
using eShopOnContainers.Core.Helpers;
namespace eShopOnContainers.Core.Services.Marketing
{
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Models.Marketing;
using RequestProvider;
public class CampaignService : ICampaignService
{
private readonly IRequestProvider _requestProvider;
public CampaignService(IRequestProvider requestProvider)
{
_requestProvider = requestProvider;
}
public async Task<ObservableCollection<CampaignItem>> GetAllCampaignsAsync(string userId, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.MarketingEndpoint);
builder.Path = $"api/v1/campaigns/user/{userId}";
string uri = builder.ToString();
CampaignRoot campaign =
await _requestProvider.GetAsync<CampaignRoot>(uri, token);
if (campaign?.Data != null)
{
ServicesHelper.FixCatalogItemPictureUri(campaign?.Data);
return campaign?.Data.ToObservableCollection();
}
return new ObservableCollection<CampaignItem>();
}
public async Task<CampaignItem> GetCampaignByIdAsync(int campaignId, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.MarketingEndpoint);
builder.Path = $"api/v1/campaigns/{campaignId}";
string uri = builder.ToString();
return await _requestProvider.GetAsync<CampaignItem>(uri, token);
}
}
}

+ 14
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/ICampaignService.cs View File

@ -0,0 +1,14 @@

namespace eShopOnContainers.Core.Services.Marketing
{
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Models.Marketing;
public interface ICampaignService
{
Task<ObservableCollection<CampaignItem>> GetAllCampaignsAsync(string userId, string token);
Task<CampaignItem> GetCampaignByIdAsync(int id, string token);
}
}

+ 4
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/IOrderService.cs View File

@ -1,13 +1,15 @@
using System.Collections.ObjectModel;
using eShopOnContainers.Core.Models.Basket;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace eShopOnContainers.Core.Services.Order
{
public interface IOrderService
{
Task CreateOrderAsync(Models.Orders.Order newOrder, string token);
//Task CreateOrderAsync(Models.Orders.Order newOrder, string token);
Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token);
Task<Models.Orders.Order> GetOrderAsync(int orderId, string token);
Task<ObservableCollection<Models.Orders.CardType>> GetCardTypesAsync(string token);
BasketCheckout MapOrderToBasket(Models.Orders.Order order);
}
}

+ 17
- 10
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderMockService.cs View File

@ -1,4 +1,5 @@
using eShopOnContainers.Core.Extensions;
using eShopOnContainers.Core.Models.Basket;
using eShopOnContainers.Core.Models.Orders;
using eShopOnContainers.Core.Models.User;
using System;
@ -64,17 +65,18 @@ namespace eShopOnContainers.Core.Services.Order
new CardType { Id = 3, Name = "MasterCard" },
};
public async Task CreateOrderAsync(Models.Orders.Order newOrder, string token)
private static BasketCheckout MockBasketCheckout = new BasketCheckout()
{
await Task.Delay(500);
if (!string.IsNullOrEmpty(token))
{
newOrder.OrderNumber = string.Format("{0}", MockOrders.Count + 1);
MockOrders.Insert(0, newOrder);
}
}
CardExpiration = DateTime.UtcNow,
CardHolderName = "FakeCardHolderName",
CardNumber = "122333423224",
CardSecurityNumber = "1234",
CardTypeId = 1,
City = "FakeCity",
Country = "FakeCountry",
ZipCode = "FakeZipCode",
Street = "FakeStreet"
};
public async Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token)
{
@ -111,5 +113,10 @@ namespace eShopOnContainers.Core.Services.Order
else
return new ObservableCollection<CardType>();
}
public BasketCheckout MapOrderToBasket(Models.Orders.Order order)
{
return MockBasketCheckout;
}
}
}

+ 18
- 12
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderService.cs View File

@ -1,4 +1,5 @@
using eShopOnContainers.Core.Services.RequestProvider;
using eShopOnContainers.Core.Models.Basket;
using eShopOnContainers.Core.Services.RequestProvider;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
@ -14,17 +15,6 @@ namespace eShopOnContainers.Core.Services.Order
_requestProvider = requestProvider;
}
public async Task CreateOrderAsync(Models.Orders.Order newOrder, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.OrdersEndpoint);
builder.Path = "api/v1/orders/new";
string uri = builder.ToString();
await _requestProvider.PostAsync(uri, newOrder, token, "x-requestid");
}
public async Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token)
{
@ -82,5 +72,21 @@ namespace eShopOnContainers.Core.Services.Order
return new ObservableCollection<Models.Orders.CardType>();
}
}
public BasketCheckout MapOrderToBasket(Models.Orders.Order order)
{
return new BasketCheckout()
{
CardExpiration = order.CardExpiration,
CardHolderName = order.CardHolderName,
CardNumber = order.CardNumber,
CardSecurityNumber = order.CardSecurityNumber,
CardTypeId = order.CardTypeId,
City = order.ShippingCity,
Country = order.ShippingCountry,
ZipCode = order.ShippingZipCode,
Street = order.ShippingStreet
};
}
}
}

+ 2
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/IRequestProvider.cs View File

@ -8,6 +8,8 @@ namespace eShopOnContainers.Core.Services.RequestProvider
Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "", string header = "");
Task<TResult> PostAsync<TResult>(string uri, string data, string clientId, string clientSecret);
Task DeleteAsync(string uri, string token = "");
}
}

+ 33
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/RequestProvider.cs View File

@ -61,6 +61,28 @@ namespace eShopOnContainers.Core.Services.RequestProvider
return result;
}
public async Task<TResult> PostAsync<TResult>(string uri, string data, string clientId, string clientSecret)
{
HttpClient httpClient = CreateHttpClient(string.Empty);
if (!string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(clientSecret))
{
AddBasicAuthenticationHeader(httpClient, clientId, clientSecret);
}
var content = new StringContent(data);
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
TResult result = await Task.Run(() =>
JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));
return result;
}
public async Task DeleteAsync(string uri, string token = "")
{
HttpClient httpClient = CreateHttpClient(token);
@ -90,6 +112,17 @@ namespace eShopOnContainers.Core.Services.RequestProvider
httpClient.DefaultRequestHeaders.Add(parameter, Guid.NewGuid().ToString());
}
private void AddBasicAuthenticationHeader(HttpClient httpClient, string clientId, string clientSecret)
{
if (httpClient == null)
return;
if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret))
return;
httpClient.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(clientId, clientSecret);
}
private async Task HandleResponse(HttpResponseMessage response)
{
if (!response.IsSuccessStatusCode)


+ 12
- 5
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs View File

@ -11,6 +11,8 @@ using eShopOnContainers.Core.Services.Identity;
using eShopOnContainers.Core.Services.Order;
using eShopOnContainers.Core.Services.User;
using Xamarin.Forms;
using eShopOnContainers.Core.Services.Location;
using eShopOnContainers.Core.Services.Marketing;
namespace eShopOnContainers.Core.ViewModels.Base
{
@ -46,22 +48,26 @@ namespace eShopOnContainers.Core.ViewModels.Base
builder.RegisterType<OrderDetailViewModel>();
builder.RegisterType<ProfileViewModel>();
builder.RegisterType<SettingsViewModel>();
builder.RegisterType<CampaignViewModel>();
builder.RegisterType<CampaignDetailsViewModel>();
// Services
builder.RegisterType<NavigationService>().As<INavigationService>().SingleInstance();
// Services
builder.RegisterType<NavigationService>().As<INavigationService>().SingleInstance();
builder.RegisterType<DialogService>().As<IDialogService>();
builder.RegisterType<OpenUrlService>().As<IOpenUrlService>();
builder.RegisterType<IdentityService>().As<IIdentityService>();
builder.RegisterType<RequestProvider>().As<IRequestProvider>();
builder.RegisterType<LocationService>().As<ILocationService>().SingleInstance();
if (useMockServices)
if (useMockServices)
{
builder.RegisterInstance(new CatalogMockService()).As<ICatalogService>();
builder.RegisterInstance(new BasketMockService()).As<IBasketService>();
builder.RegisterInstance(new OrderMockService()).As<IOrderService>();
builder.RegisterInstance(new UserMockService()).As<IUserService>();
builder.RegisterInstance(new CampaignMockService()).As<ICampaignService>();
UseMockService = true;
UseMockService = true;
}
else
{
@ -69,8 +75,9 @@ namespace eShopOnContainers.Core.ViewModels.Base
builder.RegisterType<BasketService>().As<IBasketService>().SingleInstance();
builder.RegisterType<OrderService>().As<IOrderService>().SingleInstance();
builder.RegisterType<UserService>().As<IUserService>().SingleInstance();
builder.RegisterType<CampaignService>().As<ICampaignService>().SingleInstance();
UseMockService = false;
UseMockService = false;
}
if (_container != null)


+ 42
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CampaignDetailsViewModel.cs View File

@ -0,0 +1,42 @@
namespace eShopOnContainers.Core.ViewModels
{
using System.Threading.Tasks;
using Helpers;
using Models.Marketing;
using Services.Marketing;
using Base;
public class CampaignDetailsViewModel : ViewModelBase
{
private CampaignItem _campaign;
private readonly ICampaignService _campaignService;
public CampaignDetailsViewModel(ICampaignService campaignService)
{
_campaignService = campaignService;
}
public CampaignItem Campaign
{
get => _campaign;
set
{
_campaign = value;
RaisePropertyChanged(() => Campaign);
}
}
public override async Task InitializeAsync(object navigationData)
{
if (navigationData is int)
{
IsBusy = true;
// Get campaign by id
Campaign = await _campaignService.GetCampaignByIdAsync((int) navigationData, Settings.AuthAccessToken);
IsBusy = false;
}
}
}
}

+ 49
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CampaignViewModel.cs View File

@ -0,0 +1,49 @@
namespace eShopOnContainers.Core.ViewModels
{
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using System.Collections.ObjectModel;
using Models.Marketing;
using Services.Marketing;
using Base;
using Helpers;
public class CampaignViewModel : ViewModelBase
{
private ObservableCollection<CampaignItem> _campaigns;
private readonly ICampaignService _campaignService;
public CampaignViewModel(ICampaignService campaignService)
{
_campaignService = campaignService;
}
public ObservableCollection<CampaignItem> Campaigns
{
get => _campaigns;
set
{
_campaigns = value;
RaisePropertyChanged(() => Campaigns);
}
}
public ICommand GetCampaignDetailsCommand => new Command<CampaignItem>(async (item) => await GetCampaignDetails(item));
public override async Task InitializeAsync(object navigationData)
{
IsBusy = true;
// Get campaigns by user
Campaigns = await _campaignService.GetAllCampaignsAsync(Settings.UserId, Settings.AuthAccessToken);
IsBusy = false;
}
private async Task GetCampaignDetails(CampaignItem campaign)
{
await NavigationService.NavigateToAsync<CampaignDetailsViewModel>(campaign.Id);
}
}
}

+ 4
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CheckoutViewModel.cs View File

@ -132,8 +132,10 @@ namespace eShopOnContainers.Core.ViewModels
{
var authToken = Settings.AuthAccessToken;
// Create new order
await _orderService.CreateOrderAsync(Order, authToken);
var basket = _orderService.MapOrderToBasket(Order);
// Create basket checkout
await _basketService.CheckoutAsync(basket, authToken);
// Clean Basket
await _basketService.ClearBasketAsync(_shippingAddress.Id.ToString(), authToken);


+ 13
- 9
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs View File

@ -203,20 +203,22 @@ namespace eShopOnContainers.Core.ViewModels
private void Logout()
{
var authIdToken = Settings.AuthIdToken;
var logoutRequest = _identityService.CreateLogoutRequest(authIdToken);
if(!string.IsNullOrEmpty(logoutRequest))
if (!string.IsNullOrEmpty(logoutRequest))
{
// Logout
LoginUrl = logoutRequest;
}
if(Settings.UseMocks)
if (Settings.UseMocks)
{
Settings.AuthAccessToken = string.Empty;
Settings.AuthIdToken = string.Empty;
Settings.AuthIdToken = string.Empty;
}
Settings.UserId = string.Empty;
Settings.UseFakeLocation = false;
}
private async Task NavigateAsync(string url)
@ -233,14 +235,16 @@ namespace eShopOnContainers.Core.ViewModels
else if (unescapedUrl.Contains(GlobalSetting.Instance.IdentityCallback))
{
var authResponse = new AuthorizeResponse(url);
if (!string.IsNullOrWhiteSpace(authResponse.AccessToken))
if (!string.IsNullOrWhiteSpace(authResponse.Code))
{
if (authResponse.AccessToken != null)
var userToken = await _identityService.GetTokenAsync(authResponse.Code);
string accessToken = userToken.AccessToken;
if (!string.IsNullOrWhiteSpace(accessToken))
{
Settings.AuthAccessToken = authResponse.AccessToken;
Settings.AuthAccessToken = accessToken;
Settings.AuthIdToken = authResponse.IdentityToken;
Settings.UserId = authResponse.Values["sub"];
await NavigationService.NavigateToAsync<MainViewModel>();
await NavigationService.RemoveLastFromBackStackAsync();
}


+ 146
- 16
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs View File

@ -4,38 +4,51 @@ using Xamarin.Forms;
using System.Threading.Tasks;
using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Core.Models.User;
using System;
using eShopOnContainers.Core.Models.Location;
using eShopOnContainers.Core.Services.Location;
namespace eShopOnContainers.Core.ViewModels
{
public class SettingsViewModel : ViewModelBase
{
private string _title;
private string _description;
private string _titleUseAzureServices;
private string _descriptionUseAzureServices;
private bool _useAzureServices;
private string _titleUseFakeLocation;
private string _descriptionUseFakeLocation;
private bool _useFakeLocation;
private string _endpoint;
private double _latitude;
private double _longitude;
private ILocationService _locationService;
public SettingsViewModel()
public SettingsViewModel(ILocationService locationService)
{
UseAzureServices = !Settings.UseMocks;
_useFakeLocation = Settings.UseFakeLocation;
_latitude = Settings.Latitude;
_longitude = Settings.Longitude;
_locationService = locationService;
}
public string Title
public string TitleUseAzureServices
{
get { return _title; }
get { return _titleUseAzureServices; }
set
{
_title = value;
RaisePropertyChanged(() => Title);
_titleUseAzureServices = value;
RaisePropertyChanged(() => TitleUseAzureServices);
}
}
public string Description
public string DescriptionUseAzureServices
{
get { return _description; }
get { return _descriptionUseAzureServices; }
set
{
_description = value;
RaisePropertyChanged(() => Description);
_descriptionUseAzureServices = value;
RaisePropertyChanged(() => DescriptionUseAzureServices);
}
}
@ -52,6 +65,39 @@ namespace eShopOnContainers.Core.ViewModels
}
}
public string TitleUseFakeLocation
{
get { return _titleUseFakeLocation; }
set
{
_titleUseFakeLocation = value;
RaisePropertyChanged(() => TitleUseFakeLocation);
}
}
public string DescriptionUseFakeLocation
{
get { return _descriptionUseFakeLocation; }
set
{
_descriptionUseFakeLocation = value;
RaisePropertyChanged(() => DescriptionUseFakeLocation);
}
}
public bool UseFakeLocation
{
get { return _useFakeLocation; }
set
{
_useFakeLocation = value;
// Save use fake location services to local storage
Settings.UseFakeLocation = _useFakeLocation;
RaisePropertyChanged(() => UseFakeLocation);
}
}
public string Endpoint
{
get { return _endpoint; }
@ -68,12 +114,49 @@ namespace eShopOnContainers.Core.ViewModels
}
}
public double Latitude
{
get { return _latitude; }
set
{
_latitude = value;
UpdateLatitude(_latitude);
RaisePropertyChanged(() => Latitude);
}
}
public double Longitude
{
get { return _longitude; }
set
{
_longitude = value;
UpdateLongitude(_longitude);
RaisePropertyChanged(() => Longitude);
}
}
public bool UserIsLogged => !string.IsNullOrEmpty(Settings.AuthAccessToken);
public ICommand ToggleMockServicesCommand => new Command(async () => await ToggleMockServicesAsync());
public ICommand ToggleFakeLocationCommand => new Command(() => ToggleFakeLocationAsync());
public ICommand ToggleSendLocationCommand => new Command(async () => await ToggleSendLocationAsync());
public override Task InitializeAsync(object navigationData)
{
UpdateInfo();
UpdateInfoFakeLocation();
Endpoint = Settings.UrlBase;
_latitude = Settings.Latitude;
_longitude = Settings.Longitude;
_useFakeLocation = Settings.UseFakeLocation;
return base.InitializeAsync(navigationData);
}
@ -100,17 +183,52 @@ namespace eShopOnContainers.Core.ViewModels
}
}
private void UpdateInfo()
private void ToggleFakeLocationAsync()
{
ViewModelLocator.RegisterDependencies(!UseAzureServices);
UpdateInfoFakeLocation();
}
private async Task ToggleSendLocationAsync()
{
if (!Settings.UseMocks)
{
var locationRequest = new Location
{
Latitude = _latitude,
Longitude = _longitude
};
var authToken = Settings.AuthAccessToken;
await _locationService.UpdateUserLocation(locationRequest, authToken);
}
}
private void UpdateInfo()
{
if (!UseAzureServices)
{
Title = "Use Mock Services";
Description = "Mock Services are simulated objects that mimic the behavior of real services using a controlled approach.";
TitleUseAzureServices = "Use Mock Services";
DescriptionUseAzureServices = "Mock Services are simulated objects that mimic the behavior of real services using a controlled approach.";
}
else
{
Title = "Use Microservices/Containers from eShopOnContainers";
Description = "When enabling the use of microservices/containers, the app will attempt to use real services deployed as Docker containers at the specified base endpoint, which will must be reachable through the network.";
TitleUseAzureServices = "Use Microservices/Containers from eShopOnContainers";
DescriptionUseAzureServices = "When enabling the use of microservices/containers, the app will attempt to use real services deployed as Docker containers at the specified base endpoint, which will must be reachable through the network.";
}
}
private void UpdateInfoFakeLocation()
{
if (!UseFakeLocation)
{
TitleUseFakeLocation = "Use Fake Location";
DescriptionUseFakeLocation = "Fake Location are added for marketing campaign testing.";
}
else
{
TitleUseFakeLocation = "Use Real Location";
DescriptionUseFakeLocation = "When enabling the use of real location, the app will attempt to use real location from the device.";
}
}
@ -119,5 +237,17 @@ namespace eShopOnContainers.Core.ViewModels
// Update remote endpoint (save to local storage)
Settings.UrlBase = endpoint;
}
private void UpdateLatitude(double latitude)
{
// Update fake latitude (save to local storage)
Settings.Latitude = latitude;
}
private void UpdateLongitude(double longitude)
{
// Update fake longitude (save to local storage)
Settings.Longitude = longitude;
}
}
}

+ 113
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CampaignDetailsView.xaml View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.CampaignDetailsView"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
Title="Campaign Details">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="CampaignStyle"
TargetType="{x:Type StackLayout}">
<Setter Property="VerticalOptions"
Value="Center" />
<Setter Property="Margin"
Value="0" />
</Style>
<Style x:Key="CampaignTitleStyle"
TargetType="{x:Type Label}">
<Setter Property="FontFamily"
Value="{StaticResource MontserratRegular}" />
<Setter Property="FontSize"
Value="{StaticResource MediumSize}" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="VerticalOptions"
Value="Center" />
<Setter Property="Margin"
Value="12, 0" />
</Style>
<Style x:Key="CampaignDescriptionStyle"
TargetType="{x:Type Label}"
BasedOn="{StaticResource CampaignTitleStyle}">
<Setter Property="FontSize"
Value="{StaticResource LittleSize}" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<Grid
ColumnSpacing="0"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- CAMPAIGN DETAILS -->
<ScrollView>
<StackLayout
x:Name="Campaign">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<Grid
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
BackgroundColor="Gray"/>
<StackLayout
Style ="{StaticResource CampaignStyle}"
Grid.Column="0"
Grid.Row="1">
<Image
Source="{Binding Campaign.PictureUri, Converter={StaticResource ImageConverter}}"
Aspect="AspectFit"
VerticalOptions="Start"
Margin="12,0,0,0" />
<Label
Text="{Binding Campaign.Name}"
TextColor="{StaticResource GreenColor}"
Style="{StaticResource CampaignTitleStyle}"/>
<Label
Text="{Binding Campaign.Description}"
Style="{StaticResource CampaignDescriptionStyle}"/>
<StackLayout
HorizontalOptions="Center"
Margin="12,0,0,0" >
<Label
Text="{Binding Campaign.From, StringFormat='From {0:MMMM dd, yyyy}'}"
Style="{StaticResource CampaignDescriptionStyle}"/>
<Label
Text="{Binding Campaign.To, StringFormat='until {0:MMMM dd, yyyy}'}"
Style="{StaticResource CampaignDescriptionStyle}"/>
</StackLayout>
</StackLayout>
</Grid>
</StackLayout>
</ScrollView>
<!-- INDICATOR -->
<ActivityIndicator
Grid.Row="0"
Grid.RowSpan="2"
Color="{StaticResource LightGreenColor}"
IsRunning="{Binding IsBusy}"
IsVisible="{Binding IsBusy}"
VerticalOptions="Center"
HorizontalOptions="Center">
<ActivityIndicator.WidthRequest>
<OnPlatform
x:TypeArguments="x:Double"
iOS="100"
Android="100"
WinPhone="400" />
</ActivityIndicator.WidthRequest>
</ActivityIndicator>
</Grid>
</ContentPage>

+ 12
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CampaignDetailsView.xaml.cs View File

@ -0,0 +1,12 @@
using Xamarin.Forms;
namespace eShopOnContainers.Core.Views
{
public partial class CampaignDetailsView : ContentPage
{
public CampaignDetailsView()
{
InitializeComponent();
}
}
}

+ 101
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CampaignView.xaml View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.CampaignView"
xmlns:templates="clr-namespace:eShopOnContainers.Core.Views.Templates;assembly=eShopOnContainers.Core"
xmlns:views="clr-namespace:eShopOnContainers.Core.Views;assembly=eShopOnContainers.Core"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:animations="clr-namespace:eShopOnContainers.Core.Animations;assembly=eShopOnContainers.Core"
xmlns:triggers="clr-namespace:eShopOnContainers.Core.Triggers;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
Title="Catalog">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="CampaignsListStyle"
TargetType="{x:Type ListView}">
<Setter Property="RowHeight"
Value="400" />
<Setter Property="VerticalOptions"
Value="Center" />
<Setter Property="Margin"
Value="0" />
</Style>
<animations:StoryBoard
x:Key="CampaignsAnimation"
Target="{x:Reference Campaigns}">
<animations:FadeInAnimation
Direction="Up"
Duration="1500"
Delay="250"/>
</animations:StoryBoard>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Triggers>
<EventTrigger
Event="Appearing">
<triggers:BeginAnimation
Animation="{StaticResource CampaignsAnimation}" />
</EventTrigger>
</ContentPage.Triggers>
<Grid
ColumnSpacing="0"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- CAMPAIGNS -->
<Grid
Grid.Row="1">
<Grid
IsVisible="{Binding IsBusy, Converter={StaticResource InverseBoolConverter}}">
<Label
Text="NO CAMPAIGNS FOUND"
IsVisible="{Binding Campaigns.Count, Converter={StaticResource InverseCountToBoolConverter}}"
HorizontalOptions="Center"
VerticalOptions="Center"/>
</Grid>
<ListView
x:Name="Campaigns"
IsVisible="{Binding Campaigns.Count, Converter={StaticResource CountToBoolConverter}}"
ItemsSource="{Binding Campaigns}"
HasUnevenRows="True"
SeparatorVisibility="None"
CachingStrategy="RecycleElement"
Style="{StaticResource CampaignsListStyle}">
<ListView.Behaviors>
<behaviors:EventToCommandBehavior
EventName="ItemTapped"
Command="{Binding GetCampaignDetailsCommand}"
EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}" />
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<templates:CampaignTemplate />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<!-- INDICATOR -->
<ActivityIndicator
Grid.Row="0"
Grid.RowSpan="2"
Color="{StaticResource LightGreenColor}"
IsRunning="{Binding IsBusy}"
IsVisible="{Binding IsBusy}"
VerticalOptions="Center"
HorizontalOptions="Center">
<ActivityIndicator.WidthRequest>
<OnPlatform
x:TypeArguments="x:Double"
iOS="100"
Android="100"
WinPhone="400" />
</ActivityIndicator.WidthRequest>
</ActivityIndicator>
</Grid>
</ContentPage>

+ 13
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CampaignView.xaml.cs View File

@ -0,0 +1,13 @@
namespace eShopOnContainers.Core.Views
{
using Xamarin.Forms;
public partial class CampaignView: ContentPage
{
public CampaignView()
{
InitializeComponent();
}
}
}

+ 11
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml View File

@ -63,4 +63,15 @@
WinPhone="Assets\menu_cart.png"/>
</views:BasketView.Icon>
</views:BasketView>
<!-- CAMPAIGNS -->
<views:CampaignView
x:Name="CampaignView">
<views:CampaignView.Icon>
<OnPlatform
x:TypeArguments="FileImageSource"
Android="menu_filter"
iOS="menu_filter"
WinPhone="Assets\menu_filter.png"/>
</views:CampaignView.Icon>
</views:CampaignView>
</TabbedPage>

+ 5
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml.cs View File

@ -28,12 +28,16 @@ namespace eShopOnContainers.Core.Views
case 2:
CurrentPage = BasketView;
break;
case 3:
CurrentPage = CampaignView;
break;
}
});
await ((CatalogViewModel)HomeView.BindingContext).InitializeAsync(null);
await ((BasketViewModel)BasketView.BindingContext).InitializeAsync(null);
await ((ProfileViewModel)ProfileView.BindingContext).InitializeAsync(null);
await ((CampaignViewModel)CampaignView.BindingContext).InitializeAsync(null);
}
protected override async void OnCurrentPageChanged()
@ -44,6 +48,7 @@ namespace eShopOnContainers.Core.Views
{
// Force basket view refresh every time we access it
await (BasketView.BindingContext as ViewModelBase).InitializeAsync(null);
await (CampaignView.BindingContext as ViewModelBase).InitializeAsync(null);
}
}
}


+ 78
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/SettingsView.xaml View File

@ -98,6 +98,8 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid
Grid.Row="0"
@ -108,11 +110,11 @@
Grid.Column="0"
Grid.Row="1">
<Label
Text="{Binding Title}"
Text="{Binding TitleUseAzureServices}"
TextColor="{StaticResource GreenColor}"
Style="{StaticResource SettingsTitleStyle}"/>
<Label
Text="{Binding Description}"
Text="{Binding DescriptionUseAzureServices}"
Style="{StaticResource SettingsDescriptionStyle}"/>
</StackLayout>
<!-- ON / OFF -->
@ -160,8 +162,81 @@
<Grid
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"/>
<StackLayout
Grid.Column="0"
Grid.Row="4"
IsVisible="{Binding UserIsLogged}">
<Label
Text="{Binding TitleUseFakeLocation}"
TextColor="{StaticResource GreenColor}"
Style="{StaticResource SettingsTitleStyle}"/>
<Label
Text="{Binding DescriptionUseFakeLocation}"
Style="{StaticResource SettingsDescriptionStyle}"/>
</StackLayout>
<!-- ON / OFF -->
<controls:ToggleButton
Grid.Column="1"
Grid.Row="4"
Animate="True"
Checked="{Binding UseFakeLocation, Mode=TwoWay}"
Command="{Binding ToggleFakeLocationCommand}"
Style="{StaticResource SettingsToggleButtonStyle}"
IsVisible="{Binding UserIsLogged}">
<controls:ToggleButton.CheckedImage>
<OnPlatform x:TypeArguments="ImageSource"
Android="switch_on.png"
iOS="switchOn.png"
WinPhone="Assets/switchOn.png"/>
</controls:ToggleButton.CheckedImage>
<controls:ToggleButton.UnCheckedImage>
<OnPlatform x:TypeArguments="ImageSource"
Android="switch_off.png"
iOS="switchOff.png"
WinPhone="Assets/switchOff.png"/>
</controls:ToggleButton.UnCheckedImage>
</controls:ToggleButton>
<!-- FAKE LOCATIONS -->
<StackLayout
Grid.Row="5"
Grid.Column="0"
Grid.ColumnSpan="2"
BackgroundColor="Gray"/>
Margin="12, 0, 12, 12"
IsVisible="{Binding UseFakeLocation}">
<Label
Text="Latitude"
Style="{StaticResource HeaderLabelStyle}"/>
<Entry
Text="{Binding Latitude, Mode=TwoWay}"
Keyboard="Text">
<Entry.Style>
<OnPlatform
x:TypeArguments="Style"
iOS="{StaticResource EntryStyle}"
Android="{StaticResource EntryStyle}"
WinPhone="{StaticResource UwpEntryStyle}"/>
</Entry.Style>
</Entry>
<Label
Text="Longitude"
Style="{StaticResource HeaderLabelStyle}"/>
<Entry
Text="{Binding Longitude, Mode=TwoWay}"
Keyboard="Text">
<Entry.Style>
<OnPlatform
x:TypeArguments="Style"
iOS="{StaticResource EntryStyle}"
Android="{StaticResource EntryStyle}"
WinPhone="{StaticResource UwpEntryStyle}"/>
</Entry.Style>
</Entry>
<Button
Command="{Binding ToggleSendLocationCommand}"
Text="Send Location"/>
</StackLayout>
</Grid>
</StackLayout>
</ScrollView>


+ 83
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/Templates/CampaignTemplate.xaml View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:controls="clr-namespace:eShopOnContainers.Core.Controls;assembly=eShopOnContainers.Core"
x:Class="eShopOnContainers.Core.Views.Templates.CampaignTemplate">
<ContentView.Resources>
<ResourceDictionary>
<Style x:Key="CampaignNameStyle"
TargetType="{x:Type Label}">
<Setter Property="FontFamily"
Value="{StaticResource MontserratRegular}" />
<Setter Property="FontSize"
Value="{StaticResource LargeSize}" />
<Setter Property="HorizontalOptions"
Value="Center" />
<Setter Property="Margin"
Value="0, 12, 0, 6" />
</Style>
<Style x:Key="MoreDetailsButtonStyle"
TargetType="{x:Type Grid}">
<Setter Property="HeightRequest"
Value="42" />
<Setter Property="WidthRequest"
Value="42" />
<Setter Property="HorizontalOptions"
Value="Center" />
<Setter Property="VerticalOptions"
Value="End" />
<Setter Property="Margin"
Value="0,0,0,24" />
</Style>
<Style x:Key="AddImageStyle"
TargetType="{x:Type Image}">
<Setter Property="HeightRequest"
Value="24" />
<Setter Property="WidthRequest"
Value="24" />
</Style>
</ResourceDictionary>
</ContentView.Resources>
<ContentView.Content>
<Grid
BackgroundColor="{StaticResource BackgroundColor}"
Padding="0"
Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="250" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- IMAGE -->
<ffimageloading:CachedImage
Grid.Row="0"
Source="{Binding PictureUri}"
Aspect="AspectFill">
<ffimageloading:CachedImage.LoadingPlaceholder>
<OnPlatform
x:TypeArguments="ImageSource"
iOS="default_campaign"
Android="default_campaign"
WinPhone="Assets/default_campaign.png"/>
</ffimageloading:CachedImage.LoadingPlaceholder>
<ffimageloading:CachedImage.ErrorPlaceholder>
<OnPlatform
x:TypeArguments="ImageSource"
iOS="noimage"
Android="noimage"
WinPhone="Assets/noimage.png"/>
</ffimageloading:CachedImage.ErrorPlaceholder>
</ffimageloading:CachedImage>
<!-- NAME -->
<Label
Grid.Row="1"
Text="{Binding Name, Converter={StaticResource ToUpperConverter}}"
Style="{StaticResource CampaignNameStyle}"/>
</Grid>
</ContentView.Content>
</ContentView>

+ 12
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/Templates/CampaignTemplate.xaml.cs View File

@ -0,0 +1,12 @@
using Xamarin.Forms;
namespace eShopOnContainers.Core.Views.Templates
{
public partial class CampaignTemplate : ContentView
{
public CampaignTemplate()
{
InitializeComponent();
}
}
}

+ 45
- 1
src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@ -64,12 +64,16 @@
<Compile Include="Helpers\EasingHelper.cs" />
<Compile Include="Helpers\ServicesHelper.cs" />
<Compile Include="Helpers\Settings.cs" />
<Compile Include="Models\Basket\BasketCheckout.cs" />
<Compile Include="Models\Basket\BasketItem.cs" />
<Compile Include="Models\Basket\CustomerBasket.cs" />
<Compile Include="Models\Catalog\CatalogBrand.cs" />
<Compile Include="Models\Catalog\CatalogItem.cs" />
<Compile Include="Models\Catalog\CatalogRoot.cs" />
<Compile Include="Models\Catalog\CatalogType.cs" />
<Compile Include="Models\Location\Location.cs" />
<Compile Include="Models\Marketing\CampaignItem.cs" />
<Compile Include="Models\Marketing\CampaignRoot.cs" />
<Compile Include="Models\Navigation\TabParameter.cs" />
<Compile Include="Models\Orders\CardType.CS" />
<Compile Include="Models\Orders\Order.cs" />
@ -91,6 +95,11 @@
<Compile Include="Services\Dialog\IDialogService.cs" />
<Compile Include="Services\Identity\IdentityService.cs" />
<Compile Include="Services\Identity\IIdentityService.cs" />
<Compile Include="Services\Location\ILocationService.cs" />
<Compile Include="Services\Location\LocationService.cs" />
<Compile Include="Services\Marketing\ICampaignService.cs" />
<Compile Include="Services\Marketing\CampaignMockService.cs" />
<Compile Include="Services\Marketing\CampaignService.cs" />
<Compile Include="Services\Navigation\INavigationService.cs" />
<Compile Include="Services\Navigation\NavigationService.cs" />
<Compile Include="Services\OpenUrl\IOpenUrlService.cs" />
@ -113,6 +122,8 @@
<Compile Include="ViewModels\Base\ViewModelBase.cs" />
<Compile Include="ViewModels\Base\ViewModelLocator.cs" />
<Compile Include="ViewModels\BasketViewModel.cs" />
<Compile Include="ViewModels\CampaignDetailsViewModel.cs" />
<Compile Include="ViewModels\CampaignViewModel.cs" />
<Compile Include="ViewModels\CatalogViewModel.cs" />
<Compile Include="ViewModels\CheckoutViewModel.cs" />
<Compile Include="ViewModels\LoginViewModel.cs" />
@ -123,9 +134,15 @@
<Compile Include="Views\BasketView.xaml.cs">
<DependentUpon>BasketView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\CampaignView.xaml.cs">
<DependentUpon>CampaignView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\CatalogView.xaml.cs">
<DependentUpon>CatalogView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\CampaignDetailsView.xaml.cs">
<DependentUpon>CampaignDetailsView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\CheckoutView.xaml.cs">
<DependentUpon>CheckoutView.xaml</DependentUpon>
</Compile>
@ -159,6 +176,9 @@
<Compile Include="Views\Templates\OrderTemplate.xaml.cs">
<DependentUpon>OrderTemplate.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Templates\CampaignTemplate.xaml.cs">
<DependentUpon>CampaignTemplate.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Templates\ProductTemplate.xaml.cs">
<DependentUpon>ProductTemplate.xaml</DependentUpon>
</Compile>
@ -166,6 +186,7 @@
<Compile Include="Converters\FirstValidationErrorConverter.cs" />
<Compile Include="Effects\EntryLineColorEffect.cs" />
<Compile Include="Behaviors\LineColorBehavior.cs" />
<Compile Include="Models\Token\UserToken.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
@ -259,6 +280,29 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Views\CampaignView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Reference Include="System.ComponentModel.Annotations">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.6\Profile\Profile44\System.ComponentModel.Annotations.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Views\CampaignDetailsView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Views\Templates\CampaignTemplate.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save