Browse Source

Merge remote-tracking branch 'eShopOnContainers/dev' into dev

# Conflicts:
#	k8s/deploy.ps1
pull/235/head
Eduard Tomas 7 years ago
parent
commit
9f7a7cda8a
57 changed files with 991 additions and 374 deletions
  1. +48
    -0
      README.CICD.k8s.md
  2. +0
    -29
      _docker/rabbitmq/Dockerfile.nanowin
  3. +0
    -1
      _docker/rabbitmq/enabled_plugins
  4. +0
    -1
      _docker/rabbitmq/rabbitmq.config
  5. +0
    -30
      _docker/redis/Dockerfile.nanowin
  6. +6
    -0
      cli-mac/build-bits.sh
  7. +21
    -17
      docker-compose-windows.override.yml
  8. +19
    -15
      docker-compose-windows.prod.yml
  9. +2
    -2
      docker-compose-windows.yml
  10. +2
    -2
      docker-compose.ci.build.yml
  11. +30
    -26
      docker-compose.override.yml
  12. +31
    -26
      docker-compose.prod.yml
  13. +237
    -0
      eShopOnContainers-iOS.sln
  14. BIN
      img/k8s/blob_creation.png
  15. BIN
      img/k8s/deploy_script_task.png
  16. BIN
      img/k8s/get_kubectlbin_task.png
  17. BIN
      img/k8s/get_kubectlconfig_task.png
  18. +10
    -1
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs
  19. +18
    -4
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs
  20. +10
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/SystemChecks.cs
  21. +45
    -8
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs
  22. +1
    -1
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs
  23. +8
    -8
      src/BuildingBlocks/Resilience/Resilience.Http/IHttpClient.cs
  24. +0
    -10
      src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicy.cs
  25. +121
    -26
      src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs
  26. +77
    -9
      src/BuildingBlocks/Resilience/Resilience.Http/StandardHttpClient.cs
  27. +6
    -1
      src/Services/Catalog/Catalog.API/Startup.cs
  28. +3
    -7
      src/Services/Identity/Identity.API/Configuration/Config.cs
  29. +6
    -1
      src/Services/Identity/Identity.API/Startup.cs
  30. +3
    -2
      src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs
  31. +2
    -2
      src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs
  32. +3
    -2
      src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs
  33. +25
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/Middlewares/FailingStartupFilter.cs
  34. +23
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/Middlewares/FailingWebHostBuilderExtensions.cs
  35. +1
    -0
      src/Services/Ordering/Ordering.API/Program.cs
  36. +6
    -3
      src/Services/Ordering/Ordering.API/Startup.cs
  37. +1
    -1
      src/Web/WebMVC/Controllers/OrderController.cs
  38. +68
    -0
      src/Web/WebMVC/Infrastructure/API.cs
  39. +7
    -10
      src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs
  40. +22
    -23
      src/Web/WebMVC/Services/BasketService.cs
  41. +25
    -33
      src/Web/WebMVC/Services/CatalogService.cs
  42. +34
    -33
      src/Web/WebMVC/Services/OrderingService.cs
  43. +18
    -13
      src/Web/WebMVC/Startup.cs
  44. +1
    -1
      src/Web/WebMVC/Views/Shared/_Layout.cshtml
  45. +2
    -2
      src/Web/WebMVC/WebMVC.csproj
  46. +1
    -1
      src/Web/WebMVC/wwwroot/css/site.min.css
  47. +10
    -4
      src/Web/WebSPA/Startup.cs
  48. +3
    -1
      src/Web/WebSPA/WebSPA.csproj
  49. +1
    -0
      src/Web/WebStatus/Controllers/HomeController.cs
  50. +2
    -2
      src/Web/WebStatus/Extensions/HealthCheckBuilderExtensions.cs
  51. +12
    -6
      src/Web/WebStatus/Startup.cs
  52. +10
    -3
      src/Web/WebStatus/Views/Shared/_Layout.cshtml
  53. +2
    -2
      test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs
  54. +1
    -1
      test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs
  55. +1
    -1
      test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs
  56. +3
    -1
      test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs
  57. +3
    -2
      test/Services/UnitTest/Ordering/Application/OrdersWebApiTest.cs

+ 48
- 0
README.CICD.k8s.md View File

@ -0,0 +1,48 @@
# Kubernetes CI/CD VSTS
For k8s CI/CD pipeline delivery a series of tasks must be created in VSTS to deploy k8s in Azure
## Prerequisites
* A Kubernetes cluster. Follow Azure Container Service's [walkthrough](https://docs.microsoft.com/en-us/azure/container-service/container-service-kubernetes-walkthrough) to create one.
* A private Docker registry. Follow Azure Container Registry's [guide](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal) to create one.
* Optionally, previous steps can be skipped if you run gen-k8s-env.ps1 script to automatically create the azure environment needed for kubernetes deployment. Azure cli 2.0 must be previously installed [installation guide](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli). For example:
>```
>./gen-k8s-env -resourceGroupName k8sGroup -location westeurope -registryName k8sregistry -orchestratorName k8s-cluster -dnsName k8s-dns
>```
* An `Azure Blob storage`. It is needed for storing the kubernetes config file used by the hosted agent to access to Kubernetes cluster. Example:
<img src="img/k8s/blob_creation.png">
* Upload the `kubernetes config file` to the blob storage previously created. Execute the following command which will download the config file into the directory `c:\Users\<User>\.kube\` and then, upload it to your blob storage:
>```
>https://eshopk8s.blob.core.windows.net/k8s-config/config
>```
## Create the VSTS tasks
1. Create a `Download File` task to download the kubernetes binary `kubectl` to the hosted agent. For example:
>```
>https://storage.googleapis.com/kubernetes-release/release/v0.0.1.7.0-alpha.0/bin/windows/386/kubectl.exe
>```
<img src="img/k8s/get_kubectlbin_task.png">
2. Create a Download File task to download the kubernetes config file to the hosted agent. For example:
>```
>https://eshopk8s.blob.core.windows.net/k8s-config/config
>```
<img src="img/k8s/get_kubectlconfig_task.png">
3. Create a powershell task to execute the k8s deployment script. For example:
* Deployment script path
>```
>$(System.DefaultWorkingDirectory)/All Microservices/docker-compose/deploy.ps1
>```
* Deployment script path arguments. Where:
- userDockerHub: indicates if Docker Hub is used instead of ACR
- deployCI: indicates that it is a CI/CD deployment
- execPath: path where the k8s binary is stored
- kubeconfigPath: path where the k8s config file is stored
>```
>-deployCI $true -useDockerHub $true -execPath '$(System.DefaultWorkingDirectory)/' -kubeconfigPath '$(System.DefaultWorkingDirectory)/'
>```
<img src="img/k8s/deploy_script_task.png">

+ 0
- 29
_docker/rabbitmq/Dockerfile.nanowin View File

@ -1,29 +0,0 @@
#https://github.com/spring2/dockerfiles/tree/master/rabbitmq
FROM microsoft/windowsservercore
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ENV chocolateyUseWindowsCompression false
RUN iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')); \
choco install -y curl;
RUN choco install -y erlang
ENV ERLANG_SERVICE_MANAGER_PATH="C:\Program Files\erl8.2\erts-8.2\bin"
RUN choco install -y rabbitmq
ENV RABBITMQ_SERVER="C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.5"
ENV RABBITMQ_CONFIG_FILE="c:\rabbitmq"
COPY rabbitmq.config C:/
COPY rabbitmq.config C:/Users/ContainerAdministrator/AppData/Roaming/RabbitMQ/
COPY enabled_plugins C:/Users/ContainerAdministrator/AppData/Roaming/RabbitMQ/
EXPOSE 4369
EXPOSE 5672
EXPOSE 5671
EXPOSE 15672
WORKDIR C:/Program\ Files/RabbitMQ\ Server/rabbitmq_server-3.6.5/sbin
CMD .\rabbitmq-server.bat

+ 0
- 1
_docker/rabbitmq/enabled_plugins View File

@ -1 +0,0 @@
[rabbitmq_amqp1_0,rabbitmq_management].

+ 0
- 1
_docker/rabbitmq/rabbitmq.config View File

@ -1 +0,0 @@
[{rabbit, [{loopback_users, []}]}].

+ 0
- 30
_docker/redis/Dockerfile.nanowin View File

@ -1,30 +0,0 @@
# The MSI installs a service which is hard to override, so let's use a zip file.
FROM microsoft/windowsservercore
MAINTAINER alexellis2@gmail.com
SHELL ["powershell"]
RUN $ErrorActionPreference = 'Stop'; \
wget https://github.com/MSOpenTech/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.zip -OutFile Redis-x64-3.2.100.zip ; \
Expand-Archive Redis-x64-3.2.100.zip -dest 'C:\\Program Files\\Redis\\' ; \
Remove-Item Redis-x64-3.2.100.zip -Force
RUN setx PATH '%PATH%;C:\\Program Files\\Redis\\'
WORKDIR 'C:\\Program Files\\Redis\\'
RUN Get-Content redis.windows.conf | Where { $_ -notmatch 'bind 127.0.0.1' } | Set-Content redis.openport.conf ; \
Get-Content redis.openport.conf | Where { $_ -notmatch 'protected-mode yes' } | Set-Content redis.unprotected.conf ; \
Add-Content redis.unprotected.conf 'protected-mode no' ; \
Add-Content redis.unprotected.conf 'bind 0.0.0.0' ; \
Get-Content redis.unprotected.conf
EXPOSE 6379
RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord
# Define our command to be run when launching the container
CMD .\\redis-server.exe .\\redis.unprotected.conf --port 6379 ; \
Write-Host Redis Started... ; \
while ($true) { Start-Sleep -Seconds 3600 }

+ 6
- 0
cli-mac/build-bits.sh View File

@ -10,6 +10,12 @@ projectList=(
"../src/Web/WebStatus" "../src/Web/WebStatus"
) )
pushd $(pwd)/../src/Web/WebSPA
npm install
npm rebuild node-sass
popd
for project in "${projectList[@]}" for project in "${projectList[@]}"
do do
echo -e "\e[33mWorking on $(pwd)/$project" echo -e "\e[33mWorking on $(pwd)/$project"


+ 21
- 17
docker-compose-windows.override.yml View File

@ -11,65 +11,69 @@ services:
basket.api: basket.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5103
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=basket.data - ConnectionString=basket.data
- identityUrl=http://identity.api:5105 #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.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5103:5103"
- "5103:80"
catalog.api: catalog.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5101
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
- ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5101:5101"
- "5101:80"
identity.api: identity.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5105
- ASPNETCORE_URLS=http://0.0.0.0:80
- SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104 - SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104
- ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word
- MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5105. - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5105.
ports: ports:
- "5105:5105"
- "5105:80"
ordering.api: ordering.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #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.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5102:5102"
- "5102:80"
webspa: webspa:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5104
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 - CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102 - 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. - 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 - BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
- 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.
- BasketUrlHC=http://basket.api/hc
ports: ports:
- "5104:5104"
- "5104:80"
webmvc: webmvc:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5100
- CatalogUrl=http://catalog.api:5101
- OrderingUrl=http://ordering.api:5102
- BasketUrl=http://basket.api:5103
- ASPNETCORE_URLS=http://0.0.0.0:80
- 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 #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. #Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
ports: ports:
- "5100:5100"
- "5100:80"
sql.data: sql.data:
environment: environment:


+ 19
- 15
docker-compose-windows.prod.yml View File

@ -16,61 +16,65 @@ services:
basket.api: basket.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5103
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=basket.data - ConnectionString=basket.data
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- identityUrl=http://identity.api #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
ports: ports:
- "5103:5103" - "5103:5103"
catalog.api: catalog.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5101
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
- ExternalCatalogBaseUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105. - ExternalCatalogBaseUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
ports: ports:
- "5101:5101"
- "5101:80"
identity.api: identity.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5105
- ASPNETCORE_URLS=http://0.0.0.0:80
- SpaClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5104 - SpaClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5104
- ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word
- MvcClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your host's firewall at range 5100-5105. - MvcClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your host's firewall at range 5100-5105.
ports: ports:
- "5105:5105"
- "5105:80"
ordering.api: ordering.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105. - identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
ports: ports:
- "5102:5102"
- "5102:80"
webspa: webspa:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5104
- ASPNETCORE_URLS=http://0.0.0.0
- CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 - CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102 - OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105. - IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103 - BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103
- 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.
- BasketUrlHC=http://basket.api/hc
ports: ports:
- "5104:5104"
- "5104:80"
webmvc: webmvc:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5100
- CatalogUrl=http://catalog.api:5101
- OrderingUrl=http://ordering.api:5102
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser. - IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
- BasketUrl=http://basket.api:5103
- BasketUrl=http://basket.api
ports: ports:
- "5100:5100"
- "5100:80"
sql.data: sql.data:
environment: environment:


+ 2
- 2
docker-compose-windows.yml View File

@ -58,7 +58,7 @@ services:
image: microsoft/mssql-server-windows image: microsoft/mssql-server-windows
basket.data: basket.data:
image: eshop/redis-win
image: redis:nanoserver
# build: # build:
# context: ./_docker/redis # context: ./_docker/redis
# dockerfile: Dockerfile.nanowin # dockerfile: Dockerfile.nanowin
@ -66,7 +66,7 @@ services:
- "6379:6379" - "6379:6379"
rabbitmq: rabbitmq:
image: eshop/rabbitmq-win
image: spring2/rabbitmq
# build: # build:
# context: ./_docker/rabbitmq # context: ./_docker/rabbitmq
# dockerfile: Dockerfile.nanowin # dockerfile: Dockerfile.nanowin


+ 2
- 2
docker-compose.ci.build.yml View File

@ -6,5 +6,5 @@ services:
volumes: volumes:
- .:/src - .:/src
working_dir: /src working_dir: /src
command: /bin/bash -c "dotnet restore ./eShopOnContainers-ServicesAndWebApps.sln && dotnet publish ./eShopOnContainers-ServicesAndWebApps.sln -c Release -o ./obj/Docker/publish"
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"

+ 30
- 26
docker-compose.override.yml View File

@ -11,66 +11,70 @@ services:
basket.api: basket.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5103
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=basket.data - ConnectionString=basket.data
- identityUrl=http://identity.api:5105 #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.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5103:5103"
- "5103:80"
catalog.api: catalog.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5101
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
- ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5101:5101"
- "5101:80"
identity.api: identity.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5105
- ASPNETCORE_URLS=http://0.0.0.0:80
- SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104 - SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104
- XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback #localhost do not work for UWP login, so we have to use "external" IP always - XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback #localhost do not work for UWP login, so we have to use "external" IP always
- ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word
- MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5105. - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5105.
ports: ports:
- "5105:5105"
- "5105:80"
ordering.api: ordering.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #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.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5102:5102"
- "5102:80"
webspa: webspa:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5104
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 - CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102 - 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. - 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
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
- 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.
- BasketUrlHC=http://basket.api/hc
ports: ports:
- "5104:5104"
- "5104:80"
webmvc: webmvc:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5100
- CatalogUrl=http://catalog.api:5101
- OrderingUrl=http://ordering.api:5102
- BasketUrl=http://basket.api:5103
- ASPNETCORE_URLS=http://0.0.0.0:80
- 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 #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. #Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
ports: ports:
- "5100:5100"
- "5100:80"
sql.data: sql.data:
environment: environment:
@ -82,12 +86,12 @@ services:
webstatus: webstatus:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5107
- CatalogUrl=http://catalog.api:5101/hc
- OrderingUrl=http://ordering.api:5102/hc
- BasketUrl=http://basket.api:5103/hc
- IdentityUrl=http://identity.api:5105/hc
- mvc=http://webmvc:5100/hc
- spa=http://webspa:5104/hc
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://catalog.api/hc
- OrderingUrl=http://ordering.api/hc
- BasketUrl=http://basket.api/hc
- IdentityUrl=http://identity.api/hc
- mvc=http://webmvc/hc
- spa=http://webspa/hc
ports: ports:
- "5107:5107"
- "5107:80"

+ 31
- 26
docker-compose.prod.yml View File

@ -16,65 +16,69 @@ services:
basket.api: basket.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5103
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=basket.data - ConnectionString=basket.data
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- identityUrl=http://identity.api #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5103:5103"
- "5103:80"
catalog.api: catalog.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5101
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
- ExternalCatalogBaseUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105. - ExternalCatalogBaseUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5101:5101"
- "5101:80"
identity.api: identity.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5105
- ASPNETCORE_URLS=http://0.0.0.0:80
- SpaClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5104 - SpaClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5104
- ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word
- MvcClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your host's firewall at range 5100-5105. - MvcClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your host's firewall at range 5100-5105.
- XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback - XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback
ports: ports:
- "5105:5105"
- "5105:80"
ordering.api: ordering.api:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- identityUrl=http://identity.api #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
ports: ports:
- "5102:5102"
- "5102:80"
webspa: webspa:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5104
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 - CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102 - OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105. - IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103
- BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103
- 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.
- BasketUrlHC=http://basket.api/hc
ports: ports:
- "5104:5104"
- "5104:80"
webmvc: webmvc:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5100
- CatalogUrl=http://catalog.api:5101
- OrderingUrl=http://ordering.api:5102
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser. - IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
- BasketUrl=http://basket.api:5103
- BasketUrl=http://basket.api
ports: ports:
- "5100:5100"
- "5100:80"
sql.data: sql.data:
environment: environment:
@ -86,12 +90,13 @@ services:
webstatus: webstatus:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5107
- CatalogUrl=http://catalog.api:5101/hc
- OrderingUrl=http://ordering.api:5102/hc
- BasketUrl=http://basket.api:5103/hc
- mvc=http://webmvc:5100/hc
- spa=http://webspa:5104/hc
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://catalog.api/hc
- OrderingUrl=http://ordering.api/hc
- BasketUrl=http://basket.api/hc
- IdentityUrl=http://identity.api/hc
- mvc=http://webmvc/hc
- spa=http://webspa/hc
ports: ports:
- "5107:5107"
- "5107:80"

+ 237
- 0
eShopOnContainers-iOS.sln View File

@ -0,0 +1,237 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.6
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3AF739CD-81D8-428D-A08A-0A58372DEBF6}"
ProjectSection(SolutionItems) = preProject
docker-compose.yml = docker-compose.yml
global.json = global.json
NuGet.config = NuGet.config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile Apps", "Mobile Apps", "{F61357CE-1CC2-410E-8776-B16EEBC98EB8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A857AD10-40FF-4303-BEC2-FF1C58D5735E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.Core", "src\Mobile\eShopOnContainers\eShopOnContainers.Core\eShopOnContainers.Core.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.iOS", "src\Mobile\eShopOnContainers\eShopOnContainers.iOS\eShopOnContainers.iOS.csproj", "{6EEB23DC-7063-4444-9AF8-90DF24F549C0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared Code", "Shared Code", "{778289CA-31F7-4464-8C2A-612EE846F8A7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{9CC7814B-72A6-465B-A61C-57B512DEE303}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile Apps", "Mobile Apps", "{B7B1D395-4E06-4036-BE86-C216756B9367}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.UnitTests", "src\Mobile\eShopOnContainers\eShopOnContainers.UnitTests\eShopOnContainers.UnitTests.csproj", "{F7B6A162-BC4D-4924-B16A-713F9B0344E7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.TestRunner.iOS", "src\Mobile\eShopOnContainers\eShopOnContainers.TestRunner.iOS\eShopOnContainers.TestRunner.iOS.csproj", "{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
Ad-Hoc|ARM = Ad-Hoc|ARM
Ad-Hoc|iPhone = Ad-Hoc|iPhone
Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator
Ad-Hoc|x64 = Ad-Hoc|x64
Ad-Hoc|x86 = Ad-Hoc|x86
AppStore|Any CPU = AppStore|Any CPU
AppStore|ARM = AppStore|ARM
AppStore|iPhone = AppStore|iPhone
AppStore|iPhoneSimulator = AppStore|iPhoneSimulator
AppStore|x64 = AppStore|x64
AppStore|x86 = AppStore|x86
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|iPhone = Debug|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|x64.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Ad-Hoc|x86.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|Any CPU.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|ARM.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|ARM.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|iPhone.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|x64.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|x64.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|x86.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.AppStore|x86.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|ARM.ActiveCfg = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|ARM.Build.0 = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|iPhone.Build.0 = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|x64.ActiveCfg = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|x64.Build.0 = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|x86.ActiveCfg = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|x86.Build.0 = Debug|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|Any CPU.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|ARM.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|ARM.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|iPhone.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|iPhone.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x64.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x64.Build.0 = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x86.ActiveCfg = Release|Any CPU
{67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x86.Build.0 = Release|Any CPU
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Ad-Hoc|ARM.ActiveCfg = Ad-Hoc|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Ad-Hoc|x64.ActiveCfg = Ad-Hoc|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Ad-Hoc|x86.ActiveCfg = Ad-Hoc|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.AppStore|ARM.ActiveCfg = AppStore|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.AppStore|iPhone.Build.0 = AppStore|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.AppStore|x64.ActiveCfg = AppStore|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.AppStore|x86.ActiveCfg = AppStore|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|Any CPU.Build.0 = Debug|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|ARM.ActiveCfg = Debug|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|iPhone.ActiveCfg = Debug|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|iPhone.Build.0 = Debug|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|x64.ActiveCfg = Debug|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Debug|x86.ActiveCfg = Debug|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Release|Any CPU.ActiveCfg = Release|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Release|ARM.ActiveCfg = Release|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Release|iPhone.ActiveCfg = Release|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Release|iPhone.Build.0 = Release|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Release|x64.ActiveCfg = Release|iPhone
{6EEB23DC-7063-4444-9AF8-90DF24F549C0}.Release|x86.ActiveCfg = Release|iPhone
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|x64.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Ad-Hoc|x86.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|Any CPU.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|ARM.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|ARM.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|iPhone.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|x64.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|x64.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|x86.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.AppStore|x86.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|ARM.ActiveCfg = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|ARM.Build.0 = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|iPhone.Build.0 = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|x64.ActiveCfg = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|x64.Build.0 = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|x86.ActiveCfg = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Debug|x86.Build.0 = Debug|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|Any CPU.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|ARM.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|ARM.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|iPhone.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|iPhone.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|x64.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|x64.Build.0 = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|x86.ActiveCfg = Release|Any CPU
{F7B6A162-BC4D-4924-B16A-713F9B0344E7}.Release|x86.Build.0 = Release|Any CPU
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|ARM.ActiveCfg = Ad-Hoc|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|x64.ActiveCfg = Ad-Hoc|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Ad-Hoc|x86.ActiveCfg = Ad-Hoc|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.AppStore|ARM.ActiveCfg = AppStore|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.AppStore|iPhone.Build.0 = AppStore|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.AppStore|x64.ActiveCfg = AppStore|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.AppStore|x86.ActiveCfg = AppStore|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|Any CPU.Build.0 = Debug|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|ARM.ActiveCfg = Debug|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|iPhone.ActiveCfg = Debug|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|iPhone.Build.0 = Debug|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|x64.ActiveCfg = Debug|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Debug|x86.ActiveCfg = Debug|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Release|Any CPU.ActiveCfg = Release|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Release|ARM.ActiveCfg = Release|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Release|iPhone.ActiveCfg = Release|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Release|iPhone.Build.0 = Release|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Release|x64.ActiveCfg = Release|iPhone
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}.Release|x86.ActiveCfg = Release|iPhone
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F61357CE-1CC2-410E-8776-B16EEBC98EB8} = {932D8224-11F6-4D07-B109-DA28AD288A63}
{67F9D3A8-F71E-4428-913F-C37AE82CDB24} = {778289CA-31F7-4464-8C2A-612EE846F8A7}
{6EEB23DC-7063-4444-9AF8-90DF24F549C0} = {9CC7814B-72A6-465B-A61C-57B512DEE303}
{778289CA-31F7-4464-8C2A-612EE846F8A7} = {F61357CE-1CC2-410E-8776-B16EEBC98EB8}
{9CC7814B-72A6-465B-A61C-57B512DEE303} = {F61357CE-1CC2-410E-8776-B16EEBC98EB8}
{B7B1D395-4E06-4036-BE86-C216756B9367} = {A857AD10-40FF-4303-BEC2-FF1C58D5735E}
{F7B6A162-BC4D-4924-B16A-713F9B0344E7} = {B7B1D395-4E06-4036-BE86-C216756B9367}
{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3} = {B7B1D395-4E06-4036-BE86-C216756B9367}
EndGlobalSection
EndGlobal

BIN
img/k8s/blob_creation.png View File

Before After
Width: 1119  |  Height: 319  |  Size: 26 KiB

BIN
img/k8s/deploy_script_task.png View File

Before After
Width: 1408  |  Height: 527  |  Size: 36 KiB

BIN
img/k8s/get_kubectlbin_task.png View File

Before After
Width: 1409  |  Height: 484  |  Size: 31 KiB

BIN
img/k8s/get_kubectlconfig_task.png View File

Before After
Width: 1406  |  Height: 488  |  Size: 30 KiB

+ 10
- 1
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs View File

@ -7,9 +7,18 @@ using System.Data.SqlClient;
namespace Microsoft.Extensions.HealthChecks namespace Microsoft.Extensions.HealthChecks
{ {
// REVIEW: What are the appropriate guards for these functions?
public static class HealthCheckBuilderSqlServerExtensions public static class HealthCheckBuilderSqlServerExtensions
{ {
public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString) public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return AddSqlCheck(builder, name, connectionString, builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString, TimeSpan cacheDuration)
{ {
builder.AddCheck($"SqlCheck({name})", async () => builder.AddCheck($"SqlCheck({name})", async () =>
{ {
@ -37,7 +46,7 @@ namespace Microsoft.Extensions.HealthChecks
{ {
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}"); return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}");
} }
});
}, cacheDuration);
return builder; return builder;
} }


+ 18
- 4
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs View File

@ -10,7 +10,14 @@ namespace Microsoft.Extensions.HealthChecks
{ {
// Numeric checks // Numeric checks
public static HealthCheckBuilder AddMinValueCheck<T>(this HealthCheckBuilder builder, string name, T minValue, Func<T> currentValueFunc)
public static HealthCheckBuilder AddMinValueCheck<T>(this HealthCheckBuilder builder, string name, T minValue, Func<T> currentValueFunc) where T : IComparable<T>
{
Guard.ArgumentNotNull(nameof(builder), builder);
return AddMinValueCheck(builder, name, minValue, currentValueFunc, builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddMinValueCheck<T>(this HealthCheckBuilder builder, string name, T minValue, Func<T> currentValueFunc, TimeSpan cacheDuration)
where T : IComparable<T> where T : IComparable<T>
{ {
Guard.ArgumentNotNull(nameof(builder), builder); Guard.ArgumentNotNull(nameof(builder), builder);
@ -26,12 +33,19 @@ namespace Microsoft.Extensions.HealthChecks
$"min={minValue}, current={currentValue}", $"min={minValue}, current={currentValue}",
new Dictionary<string, object> { { "min", minValue }, { "current", currentValue } } new Dictionary<string, object> { { "min", minValue }, { "current", currentValue } }
); );
});
}, cacheDuration);
return builder; return builder;
} }
public static HealthCheckBuilder AddMaxValueCheck<T>(this HealthCheckBuilder builder, string name, T maxValue, Func<T> currentValueFunc)
public static HealthCheckBuilder AddMaxValueCheck<T>(this HealthCheckBuilder builder, string name, T maxValue, Func<T> currentValueFunc) where T : IComparable<T>
{
Guard.ArgumentNotNull(nameof(builder), builder);
return AddMaxValueCheck(builder, name, maxValue, currentValueFunc, builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddMaxValueCheck<T>(this HealthCheckBuilder builder, string name, T maxValue, Func<T> currentValueFunc, TimeSpan cacheDuration)
where T : IComparable<T> where T : IComparable<T>
{ {
Guard.ArgumentNotNull(nameof(builder), builder); Guard.ArgumentNotNull(nameof(builder), builder);
@ -47,7 +61,7 @@ namespace Microsoft.Extensions.HealthChecks
$"max={maxValue}, current={currentValue}", $"max={maxValue}, current={currentValue}",
new Dictionary<string, object> { { "max", maxValue }, { "current", currentValue } } new Dictionary<string, object> { { "max", maxValue }, { "current", currentValue } }
); );
});
}, cacheDuration);
return builder; return builder;
} }


+ 10
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/SystemChecks.cs View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics; using System.Diagnostics;
namespace Microsoft.Extensions.HealthChecks namespace Microsoft.Extensions.HealthChecks
@ -12,10 +13,19 @@ namespace Microsoft.Extensions.HealthChecks
public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize) public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize)
=> AddMaxValueCheck(builder, $"PrivateMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().PrivateMemorySize64); => AddMaxValueCheck(builder, $"PrivateMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().PrivateMemorySize64);
public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration)
=> AddMaxValueCheck(builder, $"PrivateMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().PrivateMemorySize64, cacheDuration);
public static HealthCheckBuilder AddVirtualMemorySizeCheck(this HealthCheckBuilder builder, long maxSize) public static HealthCheckBuilder AddVirtualMemorySizeCheck(this HealthCheckBuilder builder, long maxSize)
=> AddMaxValueCheck(builder, $"VirtualMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().VirtualMemorySize64); => AddMaxValueCheck(builder, $"VirtualMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().VirtualMemorySize64);
public static HealthCheckBuilder AddVirtualMemorySizeCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration)
=> AddMaxValueCheck(builder, $"VirtualMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().VirtualMemorySize64, cacheDuration);
public static HealthCheckBuilder AddWorkingSetCheck(this HealthCheckBuilder builder, long maxSize) public static HealthCheckBuilder AddWorkingSetCheck(this HealthCheckBuilder builder, long maxSize)
=> AddMaxValueCheck(builder, $"WorkingSet({maxSize})", maxSize, () => Process.GetCurrentProcess().WorkingSet64); => AddMaxValueCheck(builder, $"WorkingSet({maxSize})", maxSize, () => Process.GetCurrentProcess().WorkingSet64);
public static HealthCheckBuilder AddWorkingSetCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration)
=> AddMaxValueCheck(builder, $"WorkingSet({maxSize})", maxSize, () => Process.GetCurrentProcess().WorkingSet64, cacheDuration);
} }
} }

+ 45
- 8
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs View File

@ -10,36 +10,73 @@ namespace Microsoft.Extensions.HealthChecks
{ {
public static partial class HealthCheckBuilderExtensions public static partial class HealthCheckBuilderExtensions
{ {
// URL checks
// Default URL check
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url) public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url)
=> AddUrlCheck(builder, url, response => UrlChecker.DefaultUrlCheck(response));
{
Guard.ArgumentNotNull(nameof(builder), builder);
return AddUrlCheck(builder, url, builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, TimeSpan cacheDuration)
=> AddUrlCheck(builder, url, response => UrlChecker.DefaultUrlCheck(response), cacheDuration);
// Func returning IHealthCheckResult
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url,
Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
Func<HttpResponseMessage, IHealthCheckResult> checkFunc,
TimeSpan cacheDuration)
{ {
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlCheck(builder, url, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
return AddUrlCheck(builder, url, response => new ValueTask<IHealthCheckResult>(checkFunc(response)), cacheDuration);
}
// Func returning Task<IHealthCheckResult>
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration);
} }
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url,
Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc,
TimeSpan cacheDuration)
{ {
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlCheck(builder, url, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
return AddUrlCheck(builder, url, response => new ValueTask<IHealthCheckResult>(checkFunc(response)), cacheDuration);
}
// Func returning ValueTask<IHealthCheckResult>
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration);
} }
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url,
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc,
TimeSpan cacheDuration)
{ {
Guard.ArgumentNotNull(nameof(builder), builder); Guard.ArgumentNotNull(nameof(builder), builder);
Guard.ArgumentNotNullOrEmpty(nameof(url), url); Guard.ArgumentNotNullOrEmpty(nameof(url), url);
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
var urlCheck = new UrlChecker(checkFunc, url); var urlCheck = new UrlChecker(checkFunc, url);
builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync());
builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync(), cacheDuration);
return builder; return builder;
} }
} }


+ 1
- 1
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs View File

@ -21,7 +21,7 @@ namespace Microsoft.Extensions.HealthChecks
[string.Empty] = _currentGroup [string.Empty] = _currentGroup
}; };
DefaultCacheDuration = TimeSpan.FromMinutes(1);
DefaultCacheDuration = TimeSpan.FromMinutes(5);
} }
/// <summary> /// <summary>


+ 8
- 8
src/BuildingBlocks/Resilience/Resilience.Http/IHttpClient.cs View File

@ -1,16 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
{ {
public interface IHttpClient public interface IHttpClient
{ {
HttpClient Inst { get; }
Task<string> GetStringAsync(string uri);
Task<HttpResponseMessage> PostAsync<T>(string uri, T item);
Task<HttpResponseMessage> DeleteAsync(string uri);
Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");
} }
} }

+ 0
- 10
src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicy.cs View File

@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
{
public class ResiliencePolicy
{
}
}

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

@ -3,9 +3,12 @@ using Newtonsoft.Json;
using Polly; using Polly;
using Polly.Wrap; using Polly.Wrap;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
@ -17,48 +20,140 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
/// </summary> /// </summary>
public class ResilientHttpClient : IHttpClient public class ResilientHttpClient : IHttpClient
{ {
private HttpClient _client;
private PolicyWrap _policyWrapper;
private ILogger<ResilientHttpClient> _logger;
public HttpClient Inst => _client;
private readonly HttpClient _client;
private readonly ILogger<ResilientHttpClient> _logger;
private readonly Func<string, IEnumerable<Policy>> _policyCreator;
private ConcurrentDictionary<string, PolicyWrap> _policyWrappers;
public ResilientHttpClient(Policy[] policies, ILogger<ResilientHttpClient> logger)
public ResilientHttpClient(Func<string, IEnumerable<Policy>> policyCreator, ILogger<ResilientHttpClient> logger)
{ {
_client = new HttpClient(); _client = new HttpClient();
_logger = logger; _logger = logger;
_policyCreator = policyCreator;
_policyWrappers = new ConcurrentDictionary<string, PolicyWrap>();
}
// Add Policies to be applied
_policyWrapper = Policy.WrapAsync(policies);
}
public Task<string> GetStringAsync(string uri) =>
HttpInvoker(() =>
_client.GetStringAsync(uri));
public Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
return DoPostPutAsync(HttpMethod.Post, uri, item, authorizationToken, requestId, authorizationMethod);
}
public Task<HttpResponseMessage> PostAsync<T>(string uri, T item) =>
// a new StringContent must be created for each retry
// as it is disposed after each call
HttpInvoker(() =>
public Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
return DoPostPutAsync(HttpMethod.Put, uri, item, authorizationToken, requestId, authorizationMethod);
}
public Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
var origin = GetOriginFromUri(uri);
return HttpInvoker(origin, async () =>
{ {
var response = _client.PostAsync(uri, new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json"));
// raise exception if HttpResponseCode 500
// needed for circuit breaker to track fails
if (response.Result.StatusCode == HttpStatusCode.InternalServerError)
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{ {
throw new HttpRequestException();
requestMessage.Headers.Add("x-requestid", requestId);
} }
return response;
return await _client.SendAsync(requestMessage);
}); });
}
public Task<HttpResponseMessage> DeleteAsync(string uri) =>
HttpInvoker(() => _client.DeleteAsync(uri));
public Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer")
{
var origin = GetOriginFromUri(uri);
return HttpInvoker(origin, async () =>
{
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
var response = await _client.SendAsync(requestMessage);
return await response.Content.ReadAsStringAsync();
});
}
private Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
if (method != HttpMethod.Post && method != HttpMethod.Put)
{
throw new ArgumentException("Value must be either post or put.", nameof(method));
}
// a new StringContent must be created for each retry
// as it is disposed after each call
var origin = GetOriginFromUri(uri);
return HttpInvoker(origin, () =>
{
var requestMessage = new HttpRequestMessage(method, uri);
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
}
var response = _client.SendAsync(requestMessage).Result;
// raise exception if HttpResponseCode 500
// needed for circuit breaker to track fails
if (response.StatusCode == HttpStatusCode.InternalServerError)
{
throw new HttpRequestException();
}
return Task.FromResult(response);
});
}
private async Task<T> HttpInvoker<T>(string origin, Func<Task<T>> action)
{
var normalizedOrigin = NormalizeOrigin(origin);
if (!_policyWrappers.TryGetValue(normalizedOrigin, out PolicyWrap policyWrap))
{
policyWrap = Policy.Wrap(_policyCreator(normalizedOrigin).ToArray());
_policyWrappers.TryAdd(normalizedOrigin, policyWrap);
}
private Task<T> HttpInvoker<T>(Func<Task<T>> action) =>
// Executes the action applying all // Executes the action applying all
// the policies defined in the wrapper // the policies defined in the wrapper
_policyWrapper.ExecuteAsync(() => action());
}
return await policyWrap.Execute(action, new Context(normalizedOrigin));
}
private static string NormalizeOrigin(string origin)
{
return origin?.Trim()?.ToLower();
}
private static string GetOriginFromUri(string uri)
{
var url = new Uri(uri);
var origin = $"{url.Scheme}://{url.DnsSafeHost}:{url.Port}";
return origin;
}
}
} }

+ 77
- 9
src/BuildingBlocks/Resilience/Resilience.Http/StandardHttpClient.cs View File

@ -1,7 +1,9 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
@ -10,24 +12,90 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
{ {
private HttpClient _client; private HttpClient _client;
private ILogger<StandardHttpClient> _logger; private ILogger<StandardHttpClient> _logger;
public HttpClient Inst => _client;
public StandardHttpClient(ILogger<StandardHttpClient> logger) public StandardHttpClient(ILogger<StandardHttpClient> logger)
{ {
_client = new HttpClient(); _client = new HttpClient();
_logger = logger; _logger = logger;
} }
public Task<string> GetStringAsync(string uri) =>
_client.GetStringAsync(uri);
public Task<HttpResponseMessage> PostAsync<T>(string uri, T item)
public async Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer")
{ {
var contentString = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
return _client.PostAsync(uri, contentString);
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
var response = await _client.SendAsync(requestMessage);
return await response.Content.ReadAsStringAsync();
} }
public Task<HttpResponseMessage> DeleteAsync(string uri) =>
_client.DeleteAsync(uri);
private async Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
if (method != HttpMethod.Post && method != HttpMethod.Put)
{
throw new ArgumentException("Value must be either post or put.", nameof(method));
}
// a new StringContent must be created for each retry
// as it is disposed after each call
var requestMessage = new HttpRequestMessage(HttpMethod.Post, uri);
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
}
var response = await _client.SendAsync(requestMessage);
// raise exception if HttpResponseCode 500
// needed for circuit breaker to track fails
if (response.StatusCode == HttpStatusCode.InternalServerError)
{
throw new HttpRequestException();
}
return response;
}
public async Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
return await DoPostPutAsync(HttpMethod.Post, uri, item, authorizationToken, requestId, authorizationToken);
}
public async Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
return await DoPostPutAsync(HttpMethod.Put, uri, item, authorizationToken, requestId, authorizationToken);
}
public async Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
}
return await _client.SendAsync(requestMessage);
}
} }
} }

+ 6
- 1
src/Services/Catalog/Catalog.API/Startup.cs View File

@ -50,7 +50,12 @@
services.AddHealthChecks(checks => services.AddHealthChecks(checks =>
{ {
checks.AddSqlCheck("CatalogDb", Configuration["ConnectionString"]);
var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddSqlCheck("CatalogDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes));
}); });
services.AddMvc(options => services.AddMvc(options =>


+ 3
- 7
src/Services/Identity/Identity.API/Configuration/Config.cs View File

@ -60,7 +60,7 @@ namespace Identity.API.Configuration
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
RedirectUris = { clientsUrl["Xamarin"] }, RedirectUris = { clientsUrl["Xamarin"] },
RequireConsent = false, RequireConsent = false,
PostLogoutRedirectUris = { "http://13.88.8.119:5105/Account/Redirecting", "http://10.6.1.234:5105/Account/Redirecting" },
PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" },
AllowedCorsOrigins = { "http://eshopxamarin" }, AllowedCorsOrigins = { "http://eshopxamarin" },
AllowedScopes = AllowedScopes =
{ {
@ -84,15 +84,11 @@ namespace Identity.API.Configuration
AllowOfflineAccess = true, AllowOfflineAccess = true,
RedirectUris = new List<string> RedirectUris = new List<string>
{ {
$"{clientsUrl["Mvc"]}/signin-oidc",
"http://104.40.62.65:5100/signin-oidc",
"http://localhost:5100/signin-oidc",
"http://13.88.8.119:5100/signin-oidc"
$"{clientsUrl["Mvc"]}/signin-oidc"
}, },
PostLogoutRedirectUris = new List<string> PostLogoutRedirectUris = new List<string>
{ {
$"{clientsUrl["Mvc"]}/signout-callback-oidc",
"http://localhost:5100/signout-callback-oidc"
$"{clientsUrl["Mvc"]}/signout-callback-oidc"
}, },
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {


+ 6
- 1
src/Services/Identity/Identity.API/Startup.cs View File

@ -66,7 +66,12 @@ namespace eShopOnContainers.Identity
services.AddHealthChecks(checks => services.AddHealthChecks(checks =>
{ {
checks.AddSqlCheck("Identity_Db", Configuration.GetConnectionString("DefaultConnection"));
var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddSqlCheck("Identity_Db", Configuration.GetConnectionString("DefaultConnection"), TimeSpan.FromMinutes(minutes));
}); });
services.AddTransient<IEmailSender, AuthMessageSender>(); services.AddTransient<IEmailSender, AuthMessageSender>();


+ 3
- 2
src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs View File

@ -1,13 +1,14 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries
{ {
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
public interface IOrderQueries public interface IOrderQueries
{ {
Task<dynamic> GetOrderAsync(int id); Task<dynamic> GetOrderAsync(int id);
Task<dynamic> GetOrdersAsync();
Task<IEnumerable<dynamic>> GetOrdersAsync();
Task<dynamic> GetCardTypesAsync();
Task<IEnumerable<dynamic>> GetCardTypesAsync();
} }
} }

+ 2
- 2
src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs View File

@ -44,7 +44,7 @@
} }
} }
public async Task<dynamic> GetOrdersAsync()
public async Task<IEnumerable<dynamic>> GetOrdersAsync()
{ {
using (var connection = new SqlConnection(_connectionString)) using (var connection = new SqlConnection(_connectionString))
{ {
@ -58,7 +58,7 @@
} }
} }
public async Task<dynamic> GetCardTypesAsync()
public async Task<IEnumerable<dynamic>> GetCardTypesAsync()
{ {
using (var connection = new SqlConnection(_connectionString)) using (var connection = new SqlConnection(_connectionString))
{ {


+ 3
- 2
src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs View File

@ -68,8 +68,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
[HttpGet] [HttpGet]
public async Task<IActionResult> GetOrders() public async Task<IActionResult> GetOrders()
{ {
var orders = await _orderQueries
.GetOrdersAsync();
var orderTask = _orderQueries.GetOrdersAsync();
var orders = await orderTask;
return Ok(orders); return Ok(orders);
} }


+ 25
- 0
src/Services/Ordering/Ordering.API/Infrastructure/Middlewares/FailingStartupFilter.cs View File

@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ordering.API.Infrastructure.Middlewares
{
public class FailingStartupFilter : IStartupFilter
{
public FailingStartupFilter()
{
}
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseFailingMiddleware();
next(app);
};
}
}
}

+ 23
- 0
src/Services/Ordering/Ordering.API/Infrastructure/Middlewares/FailingWebHostBuilderExtensions.cs View File

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Ordering.API.Infrastructure.Middlewares;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Hosting
{
public static class WebHostBuildertExtensions
{
public static IWebHostBuilder UseFailing(this IWebHostBuilder builder, string path)
{
builder.ConfigureServices(services =>
{
services.AddSingleton<IStartupFilter>(new FailingStartupFilter());
});
return builder;
}
}
}

+ 1
- 0
src/Services/Ordering/Ordering.API/Program.cs View File

@ -10,6 +10,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API
{ {
var host = new WebHostBuilder() var host = new WebHostBuilder()
.UseKestrel() .UseKestrel()
.UseFailing("/Failing")
.UseHealthChecks("/hc") .UseHealthChecks("/hc")
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()


+ 6
- 3
src/Services/Ordering/Ordering.API/Startup.cs View File

@ -61,7 +61,12 @@
services.AddHealthChecks(checks => services.AddHealthChecks(checks =>
{ {
checks.AddSqlCheck("OrderingDb", Configuration["ConnectionString"]);
var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddSqlCheck("OrderingDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes));
}); });
services.AddEntityFrameworkSqlServer() services.AddEntityFrameworkSqlServer()
@ -143,8 +148,6 @@
app.UseCors("CorsPolicy"); app.UseCors("CorsPolicy");
app.UseFailingMiddleware();
ConfigureAuth(app); ConfigureAuth(app);
app.UseMvcWithDefaultRoute(); app.UseMvcWithDefaultRoute();


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

@ -51,7 +51,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
} }
catch(BrokenCircuitException) catch(BrokenCircuitException)
{ {
ModelState.AddModelError("Error", "It was not possible to create a new order, please try later on");
ModelState.AddModelError("Error", "It was not possible to create a new order, please try later on. (Business Msg Due to Circuit-Breaker)");
} }
return View(model); return View(model);
} }


+ 68
- 0
src/Web/WebMVC/Infrastructure/API.cs View File

@ -0,0 +1,68 @@
namespace WebMVC.Infrastructure
{
public static class API
{
public static class Basket
{
public static string GetBasket(string baseUri, string basketId)
{
return $"{baseUri}/{basketId}";
}
public static string UpdateBasket(string baseUri)
{
return baseUri;
}
public static string CleanBasket(string baseUri, string basketId)
{
return $"{baseUri}/{basketId}";
}
}
public static class Order
{
public static string GetOrder(string baseUri, string orderId)
{
return $"{baseUri}/{orderId}";
}
public static string GetAllMyOrders(string baseUri)
{
return baseUri;
}
public static string AddNewOrder(string baseUri)
{
return $"{baseUri}/new";
}
}
public static class Catalog
{
public static string GetAllCatalogItems(string baseUri, int page, int take, int? brand, int? type)
{
var filterQs = "";
if (brand.HasValue || type.HasValue)
{
var brandQs = (brand.HasValue) ? brand.Value.ToString() : "null";
var typeQs = (type.HasValue) ? type.Value.ToString() : "null";
filterQs = $"/type/{typeQs}/brand/{brandQs}";
}
return $"{baseUri}items{filterQs}?pageIndex={page}&pageSize={take}";
}
public static string GetAllBrands(string baseUri)
{
return $"{baseUri}catalogBrands";
}
public static string GetAllTypes(string baseUri)
{
return $"{baseUri}catalogTypes";
}
}
}
}

+ 7
- 10
src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs View File

@ -1,10 +1,7 @@
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Polly; using Polly;
using System;
using System.Net.Http; using System.Net.Http;
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
@ -17,14 +14,13 @@ namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
=>_logger = logger; =>_logger = logger;
public ResilientHttpClient CreateResilientHttpClient() public ResilientHttpClient CreateResilientHttpClient()
=> new ResilientHttpClient(CreatePolicies(), _logger);
=> new ResilientHttpClient((origin) => CreatePolicies(), _logger);
private Policy[] CreatePolicies() private Policy[] CreatePolicies()
=> new Policy[] => new Policy[]
{ {
Policy.Handle<HttpRequestException>()
.WaitAndRetryAsync(
Policy.Handle<HttpRequestException>()
.WaitAndRetry(
// number of retries // number of retries
6, 6,
// exponential backofff // exponential backofff
@ -40,7 +36,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
_logger.LogDebug(msg); _logger.LogDebug(msg);
}), }),
Policy.Handle<HttpRequestException>() Policy.Handle<HttpRequestException>()
.CircuitBreakerAsync(
.CircuitBreaker(
// number of exceptions before breaking circuit // number of exceptions before breaking circuit
5, 5,
// time circuit opened before retry // time circuit opened before retry
@ -54,6 +50,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
{ {
// on circuit closed // on circuit closed
_logger.LogTrace("Circuit breaker reset"); _logger.LogTrace("Circuit breaker reset");
})};
})
};
} }
} }

+ 22
- 23
src/Web/WebMVC/Services/BasketService.cs View File

@ -5,9 +5,8 @@ using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using WebMVC.Infrastructure;
namespace Microsoft.eShopOnContainers.WebMVC.Services namespace Microsoft.eShopOnContainers.WebMVC.Services
{ {
@ -28,15 +27,13 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task<Basket> GetBasket(ApplicationUser user) public async Task<Basket> GetBasket(ApplicationUser user)
{ {
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
var token = await GetUserTokenAsync();
var getBasketUri = API.Basket.GetBasket(_remoteServiceBaseUrl, user.Id);
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var dataString = await _apiClient.GetStringAsync(getBasketUri, token);
var basketUrl = $"{_remoteServiceBaseUrl}/{user.Id}";
var dataString = await _apiClient.GetStringAsync(basketUrl);
// Use the ?? Null conditional operator to simplify the initialization of response // Use the ?? Null conditional operator to simplify the initialization of response
var response = JsonConvert.DeserializeObject<Basket>(dataString) ??
var response = JsonConvert.DeserializeObject<Basket>(dataString) ??
new Basket() new Basket()
{ {
BuyerId = user.Id BuyerId = user.Id
@ -47,14 +44,10 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task<Basket> UpdateBasket(Basket basket) public async Task<Basket> UpdateBasket(Basket basket)
{ {
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var token = await GetUserTokenAsync();
var updateBasketUri = API.Basket.UpdateBasket(_remoteServiceBaseUrl);
var basketUrl = _remoteServiceBaseUrl;
var response = await _apiClient.PostAsync(basketUrl, basket);
var response = await _apiClient.PostAsync(updateBasketUri, basket, token);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
@ -88,7 +81,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
order.OrderItems.Add(new OrderItem() order.OrderItems.Add(new OrderItem()
{ {
ProductId = int.Parse(x.ProductId), ProductId = int.Parse(x.ProductId),
PictureUrl = x.PictureUrl, PictureUrl = x.PictureUrl,
ProductName = x.ProductName, ProductName = x.ProductName,
Units = x.Quantity, Units = x.Quantity,
@ -102,7 +95,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task AddItemToBasket(ApplicationUser user, BasketItem product) public async Task AddItemToBasket(ApplicationUser user, BasketItem product)
{ {
Basket basket = await GetBasket(user);
var basket = await GetBasket(user);
if (basket == null) if (basket == null)
{ {
basket = new Basket() basket = new Basket()
@ -113,20 +107,25 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
} }
basket.Items.Add(product); basket.Items.Add(product);
await UpdateBasket(basket); await UpdateBasket(basket);
} }
public async Task CleanBasket(ApplicationUser user) public async Task CleanBasket(ApplicationUser user)
{ {
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
var token = await GetUserTokenAsync();
var cleanBasketUri = API.Basket.CleanBasket(_remoteServiceBaseUrl, user.Id);
var response = await _apiClient.DeleteAsync(cleanBasketUri, token);
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var basketUrl = $"{_remoteServiceBaseUrl}/{user.Id}";
var response = await _apiClient.DeleteAsync(basketUrl);
//CCE: response status code... //CCE: response status code...
} }
async Task<string> GetUserTokenAsync()
{
var context = _httpContextAccesor.HttpContext;
return await context.Authentication.GetTokenAsync("access_token");
}
} }
} }

+ 25
- 33
src/Web/WebMVC/Services/CatalogService.cs View File

@ -7,43 +7,32 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using WebMVC.Infrastructure;
namespace Microsoft.eShopOnContainers.WebMVC.Services namespace Microsoft.eShopOnContainers.WebMVC.Services
{ {
public class CatalogService : ICatalogService public class CatalogService : ICatalogService
{ {
private readonly IOptionsSnapshot<AppSettings> _settings; private readonly IOptionsSnapshot<AppSettings> _settings;
private IHttpClient _apiClient;
private readonly IHttpClient _apiClient;
private readonly ILogger<CatalogService> _logger;
private readonly string _remoteServiceBaseUrl; private readonly string _remoteServiceBaseUrl;
public CatalogService(IOptionsSnapshot<AppSettings> settings, ILoggerFactory loggerFactory, IHttpClient httpClient) {
public CatalogService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient, ILogger<CatalogService> logger)
{
_settings = settings; _settings = settings;
_remoteServiceBaseUrl = $"{_settings.Value.CatalogUrl}/api/v1/catalog/";
_apiClient = httpClient; _apiClient = httpClient;
var log = loggerFactory.CreateLogger("catalog service");
log.LogDebug(settings.Value.CatalogUrl);
}
public async Task<Catalog> GetCatalogItems(int page,int take, int? brand, int? type)
{
var itemsQs = $"items?pageIndex={page}&pageSize={take}";
var filterQs = "";
_logger = logger;
if (brand.HasValue || type.HasValue)
{
var brandQs = (brand.HasValue) ? brand.Value.ToString() : "null";
var typeQs = (type.HasValue) ? type.Value.ToString() : "null";
filterQs = $"/type/{typeQs}/brand/{brandQs}";
}
var catalogUrl = $"{_remoteServiceBaseUrl}items{filterQs}?pageIndex={page}&pageSize={take}";
_remoteServiceBaseUrl = $"{_settings.Value.CatalogUrl}/api/v1/catalog/";
}
var dataString = "";
public async Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type)
{
var allcatalogItemsUri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type);
//
// Using a HttpClient wrapper with Retry and Exponential Backoff
//
dataString = await _apiClient.GetStringAsync(catalogUrl);
var dataString = await _apiClient.GetStringAsync(allcatalogItemsUri);
var response = JsonConvert.DeserializeObject<Catalog>(dataString); var response = JsonConvert.DeserializeObject<Catalog>(dataString);
@ -52,14 +41,16 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task<IEnumerable<SelectListItem>> GetBrands() public async Task<IEnumerable<SelectListItem>> GetBrands()
{ {
var url = $"{_remoteServiceBaseUrl}catalogBrands";
var dataString = await _apiClient.GetStringAsync(url);
var getBrandsUri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl);
var dataString = await _apiClient.GetStringAsync(getBrandsUri);
var items = new List<SelectListItem>(); var items = new List<SelectListItem>();
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true });
JArray brands = JArray.Parse(dataString);
foreach (JObject brand in brands.Children<JObject>())
var brands = JArray.Parse(dataString);
foreach (var brand in brands.Children<JObject>())
{ {
items.Add(new SelectListItem() items.Add(new SelectListItem()
{ {
@ -73,14 +64,15 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task<IEnumerable<SelectListItem>> GetTypes() public async Task<IEnumerable<SelectListItem>> GetTypes()
{ {
var url = $"{_remoteServiceBaseUrl}catalogTypes";
var dataString = await _apiClient.GetStringAsync(url);
var getTypesUri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl);
var dataString = await _apiClient.GetStringAsync(getTypesUri);
var items = new List<SelectListItem>(); var items = new List<SelectListItem>();
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true });
JArray brands = JArray.Parse(dataString);
foreach (JObject brand in brands.Children<JObject>())
var brands = JArray.Parse(dataString);
foreach (var brand in brands.Children<JObject>())
{ {
items.Add(new SelectListItem() items.Add(new SelectListItem()
{ {


+ 34
- 33
src/Web/WebMVC/Services/OrderingService.cs View File

@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Net.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.AspNetCore.Authentication;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using WebMVC.Infrastructure;
namespace Microsoft.eShopOnContainers.WebMVC.Services namespace Microsoft.eShopOnContainers.WebMVC.Services
{ {
@ -27,15 +26,13 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
_apiClient = httpClient; _apiClient = httpClient;
} }
async public Task<Order> GetOrder(ApplicationUser user, string Id)
async public Task<Order> GetOrder(ApplicationUser user, string id)
{ {
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var token = await GetUserTokenAsync();
var getOrderUri = API.Order.GetOrder(_remoteServiceBaseUrl, id);
var dataString = await _apiClient.GetStringAsync(getOrderUri, token);
var ordersUrl = $"{_remoteServiceBaseUrl}/{Id}";
var dataString = await _apiClient.GetStringAsync(ordersUrl);
var response = JsonConvert.DeserializeObject<Order>(dataString); var response = JsonConvert.DeserializeObject<Order>(dataString);
return response; return response;
@ -43,16 +40,13 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
async public Task<List<Order>> GetMyOrders(ApplicationUser user) async public Task<List<Order>> GetMyOrders(ApplicationUser user)
{ {
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
var token = await GetUserTokenAsync();
var allMyOrdersUri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl);
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var ordersUrl = _remoteServiceBaseUrl;
var dataString = await _apiClient.GetStringAsync(ordersUrl);
var dataString = await _apiClient.GetStringAsync(allMyOrdersUri, token);
var response = JsonConvert.DeserializeObject<List<Order>>(dataString); var response = JsonConvert.DeserializeObject<List<Order>>(dataString);
return response;
return response;
} }
public Order MapUserInfoIntoOrder(ApplicationUser user, Order order) public Order MapUserInfoIntoOrder(ApplicationUser user, Order order)
@ -62,10 +56,10 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
order.State = user.State; order.State = user.State;
order.Country = user.Country; order.Country = user.Country;
order.ZipCode = user.ZipCode; order.ZipCode = user.ZipCode;
order.CardNumber = user.CardNumber; order.CardNumber = user.CardNumber;
order.CardHolderName = user.CardHolderName; order.CardHolderName = user.CardHolderName;
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]),int.Parse(user.Expiration.Split('/')[0]), 1);
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1);
order.CardSecurityNumber = user.SecurityNumber; order.CardSecurityNumber = user.SecurityNumber;
return order; return order;
@ -73,21 +67,21 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
async public Task CreateOrder(Order order) async public Task CreateOrder(Order order)
{ {
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
_apiClient.Inst.DefaultRequestHeaders.Add("x-requestid", order.RequestId.ToString());
var token = await GetUserTokenAsync();
var requestId = order.RequestId.ToString();
var addNewOrderUri = API.Order.AddNewOrder(_remoteServiceBaseUrl);
var ordersUrl = $"{_remoteServiceBaseUrl}/new";
order.CardTypeId = 1; order.CardTypeId = 1;
order.CardExpirationApiFormat(); order.CardExpirationApiFormat();
SetFakeIdToProducts(order); SetFakeIdToProducts(order);
var response = await _apiClient.PostAsync(ordersUrl, order);
var response = await _apiClient.PostAsync(addNewOrderUri, order, token, requestId);
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
throw new Exception("Error creating order, try later");
{
throw new Exception("Error creating order, try later.");
}
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
@ -106,10 +100,17 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
destination.CardSecurityNumber = original.CardSecurityNumber; destination.CardSecurityNumber = original.CardSecurityNumber;
} }
private void SetFakeIdToProducts(Order order)
void SetFakeIdToProducts(Order order)
{ {
var id = 1; var id = 1;
order.OrderItems.ForEach(x => { x.ProductId = id; id++; }); order.OrderItems.ForEach(x => { x.ProductId = id; id++; });
} }
async Task<string> GetUserTokenAsync()
{
var context = _httpContextAccesor.HttpContext;
return await context.Authentication.GetTokenAsync("access_token");
}
} }
} }

+ 18
- 13
src/Web/WebMVC/Startup.cs View File

@ -54,29 +54,34 @@ namespace Microsoft.eShopOnContainers.WebMVC
services.AddHealthChecks(checks => services.AddHealthChecks(checks =>
{ {
checks.AddUrlCheck(Configuration["CatalogUrl"]);
checks.AddUrlCheck(Configuration["OrderingUrl"]);
checks.AddUrlCheck(Configuration["BasketUrl"]);
checks.AddUrlCheck(Configuration["IdentityUrl"]);
var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddUrlCheck(Configuration["CatalogUrl"] + "/hc", TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["OrderingUrl"] + "/hc", TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["BasketUrl"] + "/hc", TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["IdentityUrl"] + "/hc", TimeSpan.FromMinutes(minutes));
}); });
// Add application services. // Add application services.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<ICatalogService, CatalogService>();
services.AddTransient<IOrderingService, OrderingService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<ICatalogService, CatalogService>();
services.AddTransient<IOrderingService, OrderingService>();
services.AddTransient<IBasketService, BasketService>(); services.AddTransient<IBasketService, BasketService>();
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString) if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
{ {
services.AddTransient<IResilientHttpClientFactory, ResilientHttpClientFactory>();
services.AddTransient<IHttpClient, ResilientHttpClient>(sp => sp.GetService<IResilientHttpClientFactory>().CreateResilientHttpClient());
services.AddSingleton<IResilientHttpClientFactory, ResilientHttpClientFactory>();
services.AddSingleton<IHttpClient, ResilientHttpClient>(sp => sp.GetService<IResilientHttpClientFactory>().CreateResilientHttpClient());
} }
else else
{ {
services.AddTransient<IHttpClient, StandardHttpClient>();
services.AddSingleton<IHttpClient, StandardHttpClient>();
} }
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
@ -113,10 +118,10 @@ namespace Microsoft.eShopOnContainers.WebMVC
AuthenticationScheme = "oidc", AuthenticationScheme = "oidc",
SignInScheme = "Cookies", SignInScheme = "Cookies",
Authority = identityUrl.ToString(), Authority = identityUrl.ToString(),
PostLogoutRedirectUri = callBackUrl.ToString(),
PostLogoutRedirectUri = callBackUrl.ToString(),
ClientId = "mvc", ClientId = "mvc",
ClientSecret = "secret", ClientSecret = "secret",
ResponseType = "code id_token",
ResponseType = "code id_token",
SaveTokens = true, SaveTokens = true,
GetClaimsFromUserInfoEndpoint = true, GetClaimsFromUserInfoEndpoint = true,
RequireHttpsMetadata = false, RequireHttpsMetadata = false,


+ 1
- 1
src/Web/WebMVC/Views/Shared/_Layout.cshtml View File

@ -55,7 +55,7 @@
</section> </section>
<section class="col-sm-6"> <section class="col-sm-6">
<div class="esh-app-footer-text hidden-xs"> e-ShoponContainers. All right reserved </div>
<div class="esh-app-footer-text hidden-xs"> e-ShoponContainers. By Microsoft Corp. </div>
</section> </section>
</article> </article>


+ 2
- 2
src/Web/WebMVC/WebMVC.csproj View File

@ -13,12 +13,12 @@
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<!--<ItemGroup>
<Compile Remove="wwwroot\lib\bootstrap\**" /> <Compile Remove="wwwroot\lib\bootstrap\**" />
<Content Remove="wwwroot\lib\bootstrap\**" /> <Content Remove="wwwroot\lib\bootstrap\**" />
<EmbeddedResource Remove="wwwroot\lib\bootstrap\**" /> <EmbeddedResource Remove="wwwroot\lib\bootstrap\**" />
<None Remove="wwwroot\lib\bootstrap\**" /> <None Remove="wwwroot\lib\bootstrap\**" />
</ItemGroup>
</ItemGroup>-->
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.0" />


+ 1
- 1
src/Web/WebMVC/wwwroot/css/site.min.css
File diff suppressed because it is too large
View File


+ 10
- 4
src/Web/WebSPA/Startup.cs View File

@ -45,10 +45,16 @@ namespace eShopConContainers.WebSPA
{ {
services.AddHealthChecks(checks => services.AddHealthChecks(checks =>
{ {
checks.AddUrlCheck(Configuration["CatalogUrl"]);
checks.AddUrlCheck(Configuration["OrderingUrl"]);
checks.AddUrlCheck(Configuration["BasketUrl"]);
checks.AddUrlCheck(Configuration["IdentityUrl"]);
var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddUrlCheck(Configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["BasketUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes));
}); });
services.Configure<AppSettings>(Configuration); services.Configure<AppSettings>(Configuration);


+ 3
- 1
src/Web/WebSPA/WebSPA.csproj View File

@ -73,12 +73,14 @@
</ItemGroup> </ItemGroup>
<!-- workaround for https://github.com/aspnet/websdk/issues/114 --> <!-- workaround for https://github.com/aspnet/websdk/issues/114 -->
<!--
<Target Name="AddGeneratedContentItems" BeforeTargets="AssignTargetPaths" DependsOnTargets="PrepareForPublish"> <Target Name="AddGeneratedContentItems" BeforeTargets="AssignTargetPaths" DependsOnTargets="PrepareForPublish">
<ItemGroup> <ItemGroup>
<Content Include="wwwroot/**" CopyToPublishDirectory="PreserveNewest" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);@(Content)" /> <Content Include="wwwroot/**" CopyToPublishDirectory="PreserveNewest" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);@(Content)" />
</ItemGroup> </ItemGroup>
</Target> </Target>
-->
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />


+ 1
- 0
src/Web/WebStatus/Controllers/HomeController.cs View File

@ -28,6 +28,7 @@ namespace WebStatus.Controllers
data.AddResult(checkResult.Key, checkResult.Value); data.AddResult(checkResult.Key, checkResult.Value);
} }
ViewBag.RefreshSeconds = 60;
return View(data); return View(data);
} }


+ 2
- 2
src/Web/WebStatus/Extensions/HealthCheckBuilderExtensions.cs View File

@ -8,11 +8,11 @@ namespace WebStatus.Extensions
{ {
public static class HealthCheckBuilderExtensions public static class HealthCheckBuilderExtensions
{ {
public static HealthCheckBuilder AddUrlCheckIfNotNull(this HealthCheckBuilder builder, string url)
public static HealthCheckBuilder AddUrlCheckIfNotNull(this HealthCheckBuilder builder, string url, TimeSpan cacheDuration)
{ {
if (!string.IsNullOrEmpty(url)) if (!string.IsNullOrEmpty(url))
{ {
builder.AddUrlCheck(url);
builder.AddUrlCheck(url, cacheDuration);
} }
return builder; return builder;


+ 12
- 6
src/Web/WebStatus/Startup.cs View File

@ -32,12 +32,18 @@ namespace WebStatus
// Add framework services. // Add framework services.
services.AddHealthChecks(checks => services.AddHealthChecks(checks =>
{ {
checks.AddUrlCheckIfNotNull(Configuration["OrderingUrl"]);
checks.AddUrlCheckIfNotNull(Configuration["BasketUrl"]);
checks.AddUrlCheckIfNotNull(Configuration["CatalogUrl"]);
checks.AddUrlCheckIfNotNull(Configuration["IdentityUrl"]);
checks.AddUrlCheckIfNotNull(Configuration["mvc"]);
checks.AddUrlCheckIfNotNull(Configuration["spa"]);
var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddUrlCheckIfNotNull(Configuration["OrderingUrl"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheckIfNotNull(Configuration["BasketUrl"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheckIfNotNull(Configuration["CatalogUrl"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheckIfNotNull(Configuration["IdentityUrl"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheckIfNotNull(Configuration["mvc"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheckIfNotNull(Configuration["spa"], TimeSpan.FromMinutes(minutes));
}); });
services.AddMvc(); services.AddMvc();
} }


+ 10
- 3
src/Web/WebStatus/Views/Shared/_Layout.cshtml View File

@ -16,6 +16,12 @@
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment> </environment>
@if (ViewBag.RefreshSeconds != null && ViewBag.RefreshSeconds > 0)
{
<meta http-equiv="refresh" content="@ViewBag.RefreshSeconds">
}
@Html.Raw(JavaScriptSnippet.FullScript) @Html.Raw(JavaScriptSnippet.FullScript)
</head> </head>
<body> <body>
@ -42,9 +48,10 @@
<div class="container body-content"> <div class="container body-content">
@RenderBody() @RenderBody()
</div> </div>
<footer class="container footer">
<p class="center">&copy; 2017 - WebStatus</p>
</footer>
<br />
<div id="footer">
<p>&nbsp;&copy; 2017 - WebStatus</p>
</div>
<environment names="Development"> <environment names="Development">


+ 2
- 2
test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs View File

@ -32,9 +32,9 @@ namespace FunctionalTests.Services.Catalog
} }
} }
public static class Post
public static class Put
{ {
public static string UpdateCatalogProduct = "api/v1/catalog/update";
public static string UpdateCatalogProduct = "api/v1/catalog/items";
} }
} }
} }

+ 1
- 1
test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs View File

@ -43,7 +43,7 @@ namespace FunctionalTests.Services
var itemToModify = basket.Items[2]; var itemToModify = basket.Items[2];
var oldPrice = itemToModify.UnitPrice; var oldPrice = itemToModify.UnitPrice;
var newPrice = oldPrice + priceModification; var newPrice = oldPrice + priceModification;
var pRes = await catalogClient.PostAsync(CatalogScenariosBase.Post.UpdateCatalogProduct, new StringContent(ChangePrice(itemToModify, newPrice, originalCatalogProducts), UTF8Encoding.UTF8, "application/json"));
var pRes = await catalogClient.PutAsync(CatalogScenariosBase.Put.UpdateCatalogProduct, new StringContent(ChangePrice(itemToModify, newPrice, originalCatalogProducts), UTF8Encoding.UTF8, "application/json"));
var modifiedCatalogProducts = await GetCatalogAsync(catalogClient); var modifiedCatalogProducts = await GetCatalogAsync(catalogClient);


+ 1
- 1
test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs View File

@ -93,7 +93,7 @@
string BuildOrderWithInvalidExperationTime() string BuildOrderWithInvalidExperationTime()
{ {
var order = new CreateOrderCommand( var order = new CreateOrderCommand(
null,
new List<OrderItemDTO>(),
cardExpiration: DateTime.UtcNow.AddYears(-1), cardExpiration: DateTime.UtcNow.AddYears(-1),
cardNumber: "5145-555-5555", cardNumber: "5145-555-5555",
cardHolderName: "Jhon Senna", cardHolderName: "Jhon Senna",


+ 3
- 1
test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs View File

@ -14,6 +14,8 @@ namespace UnitTest.Ordering.Application
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Xunit; using Xunit;
using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
public class NewOrderRequestHandlerTest public class NewOrderRequestHandlerTest
{ {
private readonly Mock<IOrderRepository> _orderRepositoryMock; private readonly Mock<IOrderRepository> _orderRepositoryMock;
@ -72,7 +74,7 @@ namespace UnitTest.Ordering.Application
private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary<string, object> args = null) private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary<string, object> args = null)
{ {
return new CreateOrderCommand( return new CreateOrderCommand(
null,
new List<OrderItemDTO>(),
city: args != null && args.ContainsKey("city") ? (string)args["city"] : null, city: args != null && args.ContainsKey("city") ? (string)args["city"] : null,
street: args != null && args.ContainsKey("street") ? (string)args["street"] : null, street: args != null && args.ContainsKey("street") ? (string)args["street"] : null,
state: args != null && args.ContainsKey("state") ? (string)args["state"] : null, state: args != null && args.ContainsKey("state") ? (string)args["state"] : null,


+ 3
- 2
test/Services/UnitTest/Ordering/Application/OrdersWebApiTest.cs View File

@ -6,6 +6,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers;
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
using Moq; using Moq;
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
@ -59,7 +60,7 @@ namespace UnitTest.Ordering.Application
public async Task Get_orders_success() public async Task Get_orders_success()
{ {
//Arrange //Arrange
var fakeDynamicResult = new Object();
var fakeDynamicResult = Enumerable.Empty<object>();
_orderQueriesMock.Setup(x => x.GetOrdersAsync()) _orderQueriesMock.Setup(x => x.GetOrdersAsync())
.Returns(Task.FromResult(fakeDynamicResult)); .Returns(Task.FromResult(fakeDynamicResult));
@ -92,7 +93,7 @@ namespace UnitTest.Ordering.Application
public async Task Get_cardTypes_success() public async Task Get_cardTypes_success()
{ {
//Arrange //Arrange
var fakeDynamicResult = new Object();
var fakeDynamicResult = Enumerable.Empty<object>();
_orderQueriesMock.Setup(x => x.GetCardTypesAsync()) _orderQueriesMock.Setup(x => x.GetCardTypesAsync())
.Returns(Task.FromResult(fakeDynamicResult)); .Returns(Task.FromResult(fakeDynamicResult));


Loading…
Cancel
Save