Merge pull request #46 from dotnet-architecture/dev

fork eShopOnContainers
This commit is contained in:
Taras Kovalenko 2019-01-22 10:54:55 +02:00 committed by GitHub
commit 6ce3769d4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
177 changed files with 1571 additions and 2931 deletions

3
.gitignore vendored
View File

@ -263,3 +263,6 @@ pub/
/src/Web/WebMVC/wwwroot/css/site.min.css
**/.kube/**
.mfractor
# Ignore HealthCheckdb
*healthchecksdb*

View File

@ -4,6 +4,9 @@
<add key="repositoryPath" value="packages" />
</config>
<packageSources>
<add key="Preview-aspnetcore-tools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json"/>
<add key="Preview-aspnetcore-dev" value="https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json"/>
<add key="Preview-aspnetcore" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json"/>
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnet-feb2017-patch/api/v3/index.json" />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
</packageSources>

View File

@ -2,15 +2,15 @@
Sample .NET Core reference application, powered by Microsoft, based on a simplified microservices architecture and Docker containers.
## IMPORTANT NOTES!
**The current supported Visual Studio version for eShopOnContainers is Visual Studio 2017 15.7** ([GA/RTM since May 8th 2018](https://docs.microsoft.com/en-us/visualstudio/releasenotes/vs2017-relnotes)) or later version.
**You can use either the latest version of Visual Studio or simply Docker CLI and .NET CLI for Windows, Mac and Linux**.
**Note for Pull Requests (PRs)**: We accept pull request from the community. When doing it, please do it onto the **DEV branch** which is the consolidated work-in-progress branch. Do not request it onto Master branch, if possible.
**NEWS / ANNOUNCEMENTS**
Do you want to be up-to-date on .NET Architecture guidance and reference apps like eShopOnContainers? --> Subscribe by "WATCHING" this new GitHub repo: https://github.com/dotnet-architecture/News
## Updated for .NET Core 2.0 and 2.1 "wave" of technologies
eShopOnContainers is updated to .NET Core 2.0 and 2.1 "wave". Not just compilation but also new recommended code in EF Core, ASP.NET Core, and other new related versions.
## Updated for .NET Core 2.2 "wave" of technologies
eShopOnContainers is updated to .NET Core 2.x (currently updated to 2.2) "wave" of technologies. Not just compilation but also new recommended code in EF Core, ASP.NET Core, and other new related versions.
The **dockerfiles** in the solution have also been updated and now support [**Docker Multi-Stage**](https://blogs.msdn.microsoft.com/stevelasker/2017/09/11/net-and-multistage-dockerfiles/) since mid-December 2017.
@ -70,7 +70,7 @@ You can download them and start reviewing these Guides/eBooks here:
| Architecting & Developing | Containers Lifecycle & CI/CD | App patterns with Xamarin.Forms |
| ------------ | ------------| ------------|
| <a href='https://aka.ms/microservicesebook'><img src="img/ebook_arch_dev_microservices_containers_cover.png"> </a> | <a href='https://aka.ms/dockerlifecycleebook'> <img src="img/ebook_containers_lifecycle.png"> </a> | <a href='https://aka.ms/xamarinpatternsebook'> <img src="img/xamarin-enterprise-patterns-ebook-cover-small.png"> </a> |
| <sup> <a href='https://aka.ms/microservicesebook'>**Download .PDF** (v2.1 Edition)</a> </sup> | <sup> <a href='https://aka.ms/dockerlifecycleebook'>**Download** </a> </sup> | <sup> <a href='https://aka.ms/xamarinpatternsebook'>**Download** </a> </sup> |
| <sup> <a href='https://aka.ms/microservicesebook'>**Download .PDF** (v2.2 Edition)</a> </sup> | <sup> <a href='https://aka.ms/dockerlifecycleebook'>**Download** </a> </sup> | <sup> <a href='https://aka.ms/xamarinpatternsebook'>**Download** </a> </sup> |
Download in other formats (**eReaders** like **MOBI**, **EPUB**) and other eBooks at the [.NET Architecture center](http://dot.net/architecture).

View File

@ -187,6 +187,17 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
- urls__basket=http://basket.api
- urls__catalog=http://catalog.api
- urls__orders=http://ordering.api
- urls__identity=http://identity.api
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc
- BasketUrlHC=http://basket.api/hc
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
- LocationUrlHC=http://locations.api/hc
ports:
- "5200:80"
volumes:
@ -196,6 +207,13 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc
- BasketUrlHC=http://basket.api/hc
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
- LocationUrlHC=http://locations.api/hc
ports:
- "5201:80"
volumes:
@ -205,6 +223,13 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc
- BasketUrlHC=http://basket.api/hc
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
- LocationUrlHC=http://locations.api/hc
ports:
- "5202:80"
volumes:
@ -214,6 +239,13 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc
- BasketUrlHC=http://basket.api/hc
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
- LocationUrlHC=http://locations.api/hc
ports:
- "5203:80"
volumes:
@ -226,6 +258,13 @@ services:
- urls__catalog=http://catalog.api
- urls__orders=http://ordering.api
- urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc
- BasketUrlHC=http://basket.api/hc
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
- LocationUrlHC=http://locations.api/hc
ports:
- "5120:80" # Important: In a production environment your should remove the external port (5120) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
@ -237,6 +276,13 @@ services:
- urls__catalog=http://catalog.api
- urls__orders=http://ordering.api
- urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc
- BasketUrlHC=http://basket.api/hc
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
- LocationUrlHC=http://locations.api/hc
ports:
- "5121:80" # Important: In a production environment your should remove the external port (5121) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
@ -259,16 +305,41 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://catalog.api/hc
- OrderingUrl=http://ordering.api/hc
- HealthChecks-UI__HealthChecks__1__Name=WebMVC HTTP Check
- HealthChecks-UI__HealthChecks__1__Uri=http://webmvc/hc
- HealthChecks-UI__HealthChecks__2__Name=WebSPA HTTP Check
- HealthChecks-UI__HealthChecks__2__Uri=http://webspa/hc
- HealthChecks-UI__HealthChecks__3__Name=Web Shopping Aggregator GW HTTP Check
- HealthChecks-UI__HealthChecks__3__Uri=http://webshoppingagg/hc
- HealthChecks-UI__HealthChecks__4__Name=Mobile Shopping Aggregator HTTP Check
- HealthChecks-UI__HealthChecks__4__Uri=http://mobileshoppingagg/hc
- HealthChecks-UI__HealthChecks__5__Name=Mobile Shopping API GW HTTP Check
- HealthChecks-UI__HealthChecks__5__Uri=http://mobileshoppingapigw/hc
- HealthChecks-UI__HealthChecks__6__Name=Mobile Marketing API GW HTTP Check
- HealthChecks-UI__HealthChecks__6__Uri=http://mobilemarketingapigw/hc
- HealthChecks-UI__HealthChecks__7__Name=Web Shopping API GW HTTP Check
- HealthChecks-UI__HealthChecks__7__Uri=http://webshoppingapigw/hc
- HealthChecks-UI__HealthChecks__8__Name=Web Marketing API GW HTTP Check
- HealthChecks-UI__HealthChecks__8__Uri=http://webmarketingapigw/hc
- HealthChecks-UI__HealthChecks__9__Name=Ordering HTTP Check
- HealthChecks-UI__HealthChecks__9__Uri=http://ordering.api/hc
- HealthChecks-UI__HealthChecks__10__Name=Ordering HTTP Background Check
- HealthChecks-UI__HealthChecks__10__Uri=http://ordering.backgroundtasks/hc
- HealthChecks-UI__HealthChecks__11__Name=Basket HTTP Check
- HealthChecks-UI__HealthChecks__11__Uri=http://basket.api/hc
- HealthChecks-UI__HealthChecks__12__Name=Catalog HTTP Check
- HealthChecks-UI__HealthChecks__12__Uri=http://catalog.api/hc
- HealthChecks-UI__HealthChecks__13__Name=Identity HTTP Check
- HealthChecks-UI__HealthChecks__13__Uri=http://identity.api/hc
- HealthChecks-UI__HealthChecks__14__Name=Marketing HTTP Check
- HealthChecks-UI__HealthChecks__14__Uri=http://marketing.api/hc
- HealthChecks-UI__HealthChecks__15__Name=Locations HTTP Check
- HealthChecks-UI__HealthChecks__15__Uri=http://locations.api/hc
- HealthChecks-UI__HealthChecks__16__Name=Payments HTTP Check
- HealthChecks-UI__HealthChecks__16__Uri=http://payment.api/hc
- HealthChecks-UI__HealthChecks__17__Name=Ordering SignalRHub HTTP Check
- HealthChecks-UI__HealthChecks__17__Uri=http://ordering.signalrhub/hc
- OrderingBackgroundTasksUrl=http://ordering.backgroundtasks/hc
- BasketUrl=http://basket.api/hc
- IdentityUrl=http://identity.api/hc
- LocationsUrl=http://locations.api/hc
- MarketingUrl=http://marketing.api/hc
- PaymentUrl=http://payment.api/hc
- mvc=http://webmvc/hc
- spa=http://webspa/hc
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
@ -281,12 +352,9 @@ services:
- 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.
- PurchaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5202
- MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5203
- 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
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
- PurchaseUrlHC=http://webshoppingapigw/hc
- MarketingUrlHC=http://webmarketingapigw/hc
- IdentityUrlHC=http://identity.api/hc
- UseCustomizationData=True
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
@ -300,14 +368,11 @@ services:
- ASPNETCORE_URLS=http://0.0.0.0:80
- PurchaseUrl=http://webshoppingapigw
- IdentityUrl=http://10.0.75.1:5105 # Local Mac: Use http://docker.for.mac.localhost:5105 || Local Windows: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. || #Remote access: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
- MarketingUrl=http://webmarketingapigw
- 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
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
- MarketingUrl=http://webmarketingapigw
- SignalrHubUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5202
- IdentityUrlHC=http://identity.api/hc
- PurchaseUrlHC=http://webshoppingapigw/hc
- MarketingUrlHC=http://webmarketingapigw/hc
- UseCustomizationData=True
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}

View File

@ -56,16 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBusRabbitMQ", "src\Bui
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationEventLogEF", "src\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj", "{9EE28E45-1533-472B-8267-56C48855BA0E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks", "HealthChecks", "{A81ECBC2-6B00-4DCD-8388-469174033379}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj", "{942ED6E8-0050-495F-A0EA-01E97F63760C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj", "{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.SqlServer", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj", "{4BD76717-3102-4969-8C2C-BAAA3F0263B6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Location", "Location", "{41139F64-4046-4F16-96B7-D941D96FA9C6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Locations.API", "src\Services\Location\Locations.API\Locations.API.csproj", "{E7581357-FC34-474C-B8F5-307EE3CE05EF}"
@ -76,8 +68,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.API", "src\Servic
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBusServiceBus", "src\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj", "{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.AzureStorage", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.AzureStorage\Microsoft.Extensions.HealthChecks.AzureStorage.csproj", "{768C887F-C229-4B94-ACD8-0C7F65686524}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebHost", "WebHost", "{1815B651-941C-466B-AE33-D1D7EEB8F77F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebHost.Customization", "src\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj", "{15F4B3AA-89B6-4A0D-9051-414305974781}"
@ -644,54 +634,6 @@ Global
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x64.Build.0 = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.ActiveCfg = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|ARM.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhone.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x64.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x64.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x86.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x86.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|ARM.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhone.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x64.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x64.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x86.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x86.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|Any CPU.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|ARM.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|ARM.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhone.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhone.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x64.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x64.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -740,102 +682,6 @@ Global
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.Build.0 = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|ARM.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhone.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x64.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x64.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x86.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x86.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|ARM.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|ARM.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhone.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x64.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x64.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x86.ActiveCfg = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x86.Build.0 = Debug|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|Any CPU.Build.0 = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|ARM.ActiveCfg = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|ARM.Build.0 = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhone.ActiveCfg = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhone.Build.0 = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x64.ActiveCfg = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x64.Build.0 = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x86.ActiveCfg = Release|Any CPU
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x86.Build.0 = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|ARM.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhone.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x64.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x64.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x86.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x86.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|ARM.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|ARM.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhone.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x64.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x64.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x86.ActiveCfg = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x86.Build.0 = Debug|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|Any CPU.Build.0 = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|ARM.ActiveCfg = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|ARM.Build.0 = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhone.ActiveCfg = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhone.Build.0 = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x64.ActiveCfg = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x64.Build.0 = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.ActiveCfg = Release|Any CPU
{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.Build.0 = Release|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -980,54 +826,6 @@ Global
{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x64.Build.0 = Release|Any CPU
{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x86.ActiveCfg = Release|Any CPU
{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x86.Build.0 = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|ARM.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|iPhone.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|x64.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|x64.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|x86.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|x86.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|Any CPU.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|ARM.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|ARM.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|iPhone.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|x64.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|x64.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|x86.ActiveCfg = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|x86.Build.0 = Debug|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|Any CPU.ActiveCfg = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|Any CPU.Build.0 = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|ARM.ActiveCfg = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|ARM.Build.0 = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|iPhone.ActiveCfg = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|iPhone.Build.0 = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|x64.ActiveCfg = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|x64.Build.0 = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|x86.ActiveCfg = Release|Any CPU
{768C887F-C229-4B94-ACD8-0C7F65686524}.Release|x86.Build.0 = Release|Any CPU
{15F4B3AA-89B6-4A0D-9051-414305974781}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{15F4B3AA-89B6-4A0D-9051-414305974781}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{15F4B3AA-89B6-4A0D-9051-414305974781}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -1869,17 +1667,12 @@ Global
{0044B293-1DCC-4224-B948-00CF6DC7F510} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{8088F3FC-6787-45FA-A924-816EC81CBFAC} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{9EE28E45-1533-472B-8267-56C48855BA0E} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{41139F64-4046-4F16-96B7-D941D96FA9C6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{E7581357-FC34-474C-B8F5-307EE3CE05EF} = {41139F64-4046-4F16-96B7-D941D96FA9C6}
{A5260DE0-1FDD-467E-9CC1-A028AB081CEE} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{DF395F85-B010-465D-857A-7EBCC512C0C2} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE}
{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{768C887F-C229-4B94-ACD8-0C7F65686524} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{1815B651-941C-466B-AE33-D1D7EEB8F77F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{15F4B3AA-89B6-4A0D-9051-414305974781} = {1815B651-941C-466B-AE33-D1D7EEB8F77F}
{77849D35-37D4-4802-81DC-9477B2775A40} = {932D8224-11F6-4D07-B109-DA28AD288A63}

View File

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

View File

@ -11,4 +11,11 @@ metadata:
heritage: {{ .Release.Service }}
data:
internalurls__identity: http://{{ .Values.app.svc.identity }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc

View File

@ -32,6 +32,20 @@ env:
configmap:
- name: IdentityUrl
key: internalurls__identity
- name: CatalogUrlHC
key: internalurls__catalog__hc
- name: BasketUrlHC
key: internalurls__basket__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: OrderingUrlHC
key: internalurls__ordering__hc
- name: MarketingUrlHC
key: internalurls__marketing__hc
- name: PaymentUrlHC
key: internalurls__payment__hc
- name: LocationUrlHC
key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT

View File

@ -11,4 +11,11 @@ metadata:
heritage: {{ .Release.Service }}
data:
internalurls__identity: http://{{ .Values.app.svc.identity }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc

View File

@ -32,6 +32,20 @@ env:
configmap:
- name: IdentityUrl
key: internalurls__identity
- name: CatalogUrlHC
key: internalurls__catalog__hc
- name: BasketUrlHC
key: internalurls__basket__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: OrderingUrlHC
key: internalurls__ordering__hc
- name: MarketingUrlHC
key: internalurls__marketing__hc
- name: PaymentUrlHC
key: internalurls__payment__hc
- name: LocationUrlHC
key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT

View File

@ -11,4 +11,11 @@ metadata:
heritage: {{ .Release.Service }}
data:
internalurls__identity: http://{{ .Values.app.svc.identity }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc

View File

@ -32,6 +32,20 @@ env:
configmap:
- name: IdentityUrl
key: internalurls__identity
- name: CatalogUrlHC
key: internalurls__catalog__hc
- name: BasketUrlHC
key: internalurls__basket__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: OrderingUrlHC
key: internalurls__ordering__hc
- name: MarketingUrlHC
key: internalurls__marketing__hc
- name: PaymentUrlHC
key: internalurls__payment__hc
- name: LocationUrlHC
key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT

View File

@ -11,4 +11,11 @@ metadata:
heritage: {{ .Release.Service }}
data:
internalurls__identity: http://{{ .Values.app.svc.identity }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc

View File

@ -32,6 +32,20 @@ env:
configmap:
- name: IdentityUrl
key: internalurls__identity
- name: CatalogUrlHC
key: internalurls__catalog__hc
- name: BasketUrlHC
key: internalurls__basket__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: OrderingUrlHC
key: internalurls__ordering__hc
- name: MarketingUrlHC
key: internalurls__marketing__hc
- name: PaymentUrlHC
key: internalurls__payment__hc
- name: LocationUrlHC
key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT

View File

@ -16,7 +16,7 @@ $dns = $externalDns
$ingressValuesFile="ingress_values.yaml"
if ($ingressValuesFile) {
if ($useLocalk8s -eq $true) {
$ingressValuesFile="ingress_values_dockerk8s.yaml"
$dns="localhost"
}

View File

@ -14,6 +14,8 @@ inf:
db: OrderingDb # Ordering API SQL db name
identity:
db: IdentityDb # Ordering API SQL db name
marketing:
db: MarketingDb # Marketing API SQL db name
mongo:
# host: my-nosql-data # Uncomment to use specify custom mongo host. By default nosql-data is used
locations:

View File

@ -2,6 +2,7 @@
{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}}
{{- $webshoppingapigw := include "url-of" (list .Values.app.ingress.entries.webshoppingapigw .) -}}
{{- $mongo := include "mongo-name" . -}}
{{- $sqlsrv := include "sql-name" . -}}
apiVersion: v1
kind: ConfigMap
@ -19,6 +20,7 @@ data:
all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}"
internalurls__IdentityUrl: http://{{ .Values.app.svc.identity }}
urls__IdentityUrl: {{ $identity }}
marketing__ConnectionString: mongodb://{{ $mongo }}
marketing__Database: {{ .Values.inf.mongo.marketing.database }}
marketing__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.marketing.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
marketing__MongoConnectionString: mongodb://{{ $mongo }}
marketing__MongoDatabase: {{ .Values.inf.mongo.marketing.database }}
marketing__PicBaseUrl: http://{{ $webshoppingapigw }}/api/v1/c/catalog/items/[0]/pic/

View File

@ -44,8 +44,10 @@ env:
key: urls__IdentityUrl
- name: ConnectionString
key: marketing__ConnectionString
- name: Database
key: marketing__Database
- name: MongoConnectionString
key: marketing__MongoConnectionString
- name: MongoDatabase
key: marketing__MongoDatabase
- name: PicBaseUrl
key: marketing__PicBaseUrl
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)

View File

@ -17,3 +17,10 @@ data:
internalurls__catalog: http://{{ .Values.app.svc.catalog }}
internalurls__identity: http://{{ .Values.app.svc.identity }}
internalurls__ordering: http://{{ .Values.app.svc.ordering }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc

View File

@ -40,6 +40,20 @@ env:
key: internalurls__ordering
- name: urls__identity
key: internalurls__identity
- name: CatalogUrlHC
key: internalurls__catalog__hc
- name: BasketUrlHC
key: internalurls__basket__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: OrderingUrlHC
key: internalurls__ordering__hc
- name: MarketingUrlHC
key: internalurls__marketing__hc
- name: PaymentUrlHC
key: internalurls__payment__hc
- name: LocationUrlHC
key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT

View File

@ -21,12 +21,9 @@ data:
webmvc__keystore: {{ .Values.inf.redis.keystore.constr }}
internalurls__apigwws: http://{{ .Values.app.svc.webshoppingapigw }}
internalurls__apigwwm: http://{{ .Values.app.svc.webmarketingapigw }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__apigwws__hc: http://{{ .Values.app.svc.webshoppingapigw }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__apigwwm__hc: http://{{ .Values.app.svc.webmarketingapigw }}/hc
urls__apigwws: http://{{ $webshoppingapigw }}
urls__mvc: http://{{ $mvc }}
urls__IdentityUrl: http://{{ $identity }}

View File

@ -46,18 +46,12 @@ env:
key: urls__IdentityUrl
- name: MarketingUrl
key: internalurls__apigwwm
- name: BasketUrlHC
key: internalurls__basket__hc
- name: CatalogUrlHC
key: internalurls__catalog__hc
- name: PurchaseUrlHC
key: internalurls__apigwws__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: OrderingUrlHC
key: internalurls__ordering__hc
- name: MarketingUrlHC
key: internalurls__marketing__hc
- name: PaymentUrlHC
key: internalurls__payment__hc
key: internalurls__apigwwm__hc
- name: SignalrHubUrl
key: urls__apigwws
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)

View File

@ -17,3 +17,10 @@ data:
internalurls__catalog: http://{{ .Values.app.svc.catalog }}
internalurls__identity: http://{{ .Values.app.svc.identity }}
internalurls__ordering: http://{{ .Values.app.svc.ordering }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc

View File

@ -40,6 +40,20 @@ env:
key: internalurls__ordering
- name: urls__identity
key: internalurls__identity
- name: CatalogUrlHC
key: internalurls__catalog__hc
- name: BasketUrlHC
key: internalurls__basket__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: OrderingUrlHC
key: internalurls__ordering__hc
- name: MarketingUrlHC
key: internalurls__marketing__hc
- name: PaymentUrlHC
key: internalurls__payment__hc
- name: LocationUrlHC
key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT

View File

@ -20,12 +20,9 @@ data:
all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}"
webspa__keystore: {{ .Values.inf.redis.keystore.constr }}
internalurls__apigwws: http://{{ .Values.app.svc.webshoppingapigw }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__apigwws__hc: http://{{ .Values.app.svc.webshoppingapigw }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__apigwwm__hc: http://{{ .Values.app.svc.webmarketingapigw }}/hc
urls__apigwws: http://{{ $webshoppingapigw }}
urls__spa: http://{{ $spa }}
urls__IdentityUrl: http://{{ $identity }}

View File

@ -44,18 +44,12 @@ env:
key: urls__IdentityUrl
- name: MarketingUrl
key: urls__apigwwm
- name: BasketUrlHC
key: internalurls__basket__hc
- name: CatalogUrlHC
key: internalurls__catalog__hc
- name: PurchaseUrlHC
key: internalurls__apigwws__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: OrderingUrlHC
key: internalurls__ordering__hc
- name: MarketingUrlHC
key: internalurls__marketing__hc
- name: PaymentUrlHC
key: internalurls__payment__hc
key: internalurls__apigwwm__hc
- name: SignalrHubUrl
key: urls__apigwws
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)

View File

@ -19,18 +19,37 @@ data:
all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}"
all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}"
webstatus__keystore: {{ .Values.inf.redis.keystore.constr }}
internalurls__apigwws: http://{{ .Values.app.svc.webshoppingapigw }}
internalurls__apigwwm: http://{{ .Values.app.svc.webmarketingapigw }}
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
internalurls__locations__hc: http://{{ .Values.app.svc.locations }}/hc
internalurls__orderingbackground__hc: http://{{ .Values.app.svc.orderingbackgroundtasks }}/hc
name__mvc__hc: WebMVC HTTP Check
internalurls__mvc__hc: http://{{ .Values.app.svc.mvc }}/hc
name__spa__hc: WebSPA HTTP Check
internalurls__spa__hc: http://{{ .Values.app.svc.spa }}/hc
urls__apigwws: http://{{ $webshoppingapigw }}
urls__mvc: http://{{ $mvc }}
urls__IdentityUrl: http://{{ $identity }}
name__apigwws__hc: Web Shopping API GW HTTP Check
internalurls__apigwws__hc: http://{{ .Values.app.svc.webshoppingapigw }}/hc
name__apigwwm__hc: Web Marketing API GW HTTP Check
internalurls__apigwwm__hc: http://{{ .Values.app.svc.webmarketingapigw }}/hc
name__apigwms__hc: Mobile Shopping API GW HTTP Check
internalurls__apigwms__hc: http://{{ .Values.app.svc.mobileshoppingapigw }}/hc
name__apigwmm__hc: Mobile Marketing API GW HTTP Check
internalurls__apigwmm__hc: http://{{ .Values.app.svc.mobilemarketingapigw }}/hc
name__apigwwsagg__hc: Web Shopping Aggregator GW HTTP Check
internalurls__apigwwsagg__hc: http://{{ .Values.app.svc.webshoppingagg }}/hc
name__apigwmsagg__hc: Mobile Shopping Aggregator HTTP Check
internalurls__apigwmsagg__hc: http://{{ .Values.app.svc.mobileshoppingagg }}/hc
name__ordering__hc: Ordering HTTP Check
internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
name__orderingbackground__hc: Ordering HTTP Background Check
internalurls__orderingbackground__hc: http://{{ .Values.app.svc.orderingbackgroundtasks }}/hc
name__basket__hc: Basket HTTP Check
internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
name__catalog__hc: Catalog HTTP Check
internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
name__identity__hc: Identity HTTP Check
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
name__marketing__hc: Marketing HTTP Check
internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
name__locations__hc: Locations HTTP Check
internalurls__locations__hc: http://{{ .Values.app.svc.locations }}/hc
name__payment__hc: Payment HTTP Check
internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
name__signalrhub__hc: Ordering SignalR Hub HTTP Check
internalurls__signalrhub__hc: http://{{ .Values.app.svc.orderingsignalrhub }}/hc

View File

@ -13,7 +13,9 @@ service:
ingress:
enabled: true
annotations: {}
annotations: {
}
tls: []
resources: {}
@ -32,27 +34,73 @@ env:
configmap:
- name: ApplicationInsights__InstrumentationKey
key: all__InstrumentationKey
- name: BasketUrl
key: internalurls__basket__hc
- name: CatalogUrl
key: internalurls__catalog__hc
- name: IdentityUrl
key: internalurls__identity__hc
- name: OrderingUrl
key: internalurls__ordering__hc
- name: OrderingBackgroundTasksUrl
key: internalurls__orderingbackground__hc
- name: LocationsUrl
key: internalurls__locations__hc
- name: MarketingUrl
key: internalurls__marketing__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- name: mvc
- name: HealthChecks-UI__HealthChecks__0__Name
key: name__mvc__hc
- name: HealthChecks-UI__HealthChecks__0__Uri
key: internalurls__mvc__hc
- name: spa
- name: HealthChecks-UI__HealthChecks__1__Name
key: name__spa__hc
- name: HealthChecks-UI__HealthChecks__1__Uri
key: internalurls__spa__hc
- name: PaymentUrl
- name: HealthChecks-UI__HealthChecks__2__Name
key: name__apigwws__hc
- name: HealthChecks-UI__HealthChecks__2__Uri
key: internalurls__apigwws__hc
- name: HealthChecks-UI__HealthChecks__3__Name
key: name__apigwwm__hc
- name: HealthChecks-UI__HealthChecks__3__Uri
key: internalurls__apigwwm__hc
- name: HealthChecks-UI__HealthChecks__4__Name
key: name__apigwms__hc
- name: HealthChecks-UI__HealthChecks__4__Uri
key: internalurls__apigwms__hc
- name: HealthChecks-UI__HealthChecks__5__Name
key: name__apigwmm__hc
- name: HealthChecks-UI__HealthChecks__5__Uri
key: internalurls__apigwmm__hc
- name: HealthChecks-UI__HealthChecks__6__Name
key: name__apigwwsagg__hc
- name: HealthChecks-UI__HealthChecks__6__Uri
key: internalurls__apigwwsagg__hc
- name: HealthChecks-UI__HealthChecks__7__Name
key: name__apigwmsagg__hc
- name: HealthChecks-UI__HealthChecks__7__Uri
key: internalurls__apigwmsagg__hc
- name: HealthChecks-UI__HealthChecks__8__Name
key: name__ordering__hc
- name: HealthChecks-UI__HealthChecks__8__Uri
key: internalurls__ordering__hc
- name: HealthChecks-UI__HealthChecks__9__Name
key: name__orderingbackground__hc
- name: HealthChecks-UI__HealthChecks__9__Uri
key: internalurls__orderingbackground__hc
- name: HealthChecks-UI__HealthChecks__10__Name
key: name__signalrhub__hc
- name: HealthChecks-UI__HealthChecks__10__Uri
key: internalurls__signalrhub__hc
- name: HealthChecks-UI__HealthChecks__11__Name
key: name__basket__hc
- name: HealthChecks-UI__HealthChecks__11__Uri
key: internalurls__basket__hc
- name: HealthChecks-UI__HealthChecks__12__Name
key: name__catalog__hc
- name: HealthChecks-UI__HealthChecks__12__Uri
key: internalurls__catalog__hc
- name: HealthChecks-UI__HealthChecks__13__Name
key: name__identity__hc
- name: HealthChecks-UI__HealthChecks__13__Uri
key: internalurls__identity__hc
- name: HealthChecks-UI__HealthChecks__14__Name
key: name__marketing__hc
- name: HealthChecks-UI__HealthChecks__14__Uri
key: internalurls__marketing__hc
- name: HealthChecks-UI__HealthChecks__15__Name
key: name__locations__hc
- name: HealthChecks-UI__HealthChecks__15__Uri
key: internalurls__locations__hc
- name: HealthChecks-UI__HealthChecks__16__Name
key: name__payment__hc
- name: HealthChecks-UI__HealthChecks__16__Uri
key: internalurls__payment__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:

View File

@ -1,13 +1,12 @@
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.1-sdk AS build
FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/
RUN dotnet restore src/ApiGateways/ApiGw-Base/
COPY . .
WORKDIR /src/src/ApiGateways/ApiGw-Base/
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build -c Release -o /app
FROM build AS publish

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
@ -9,7 +9,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
<PackageReference Include="Ocelot" Version="12.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />

View File

@ -1,16 +1,19 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using System;
using HealthChecks.UI.Client;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace OcelotApiGw
{
public class Startup
{
private readonly IConfiguration _cfg;
public Startup(IConfiguration configuration)
@ -23,12 +26,23 @@ namespace OcelotApiGw
var identityUrl = _cfg.GetValue<string>("IdentityUrl");
var authenticationProviderKey = "IdentityApiKey";
services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddUrlGroup(new Uri(_cfg["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
.AddUrlGroup(new Uri(_cfg["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
.AddUrlGroup(new Uri(_cfg["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" })
.AddUrlGroup(new Uri(_cfg["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
.AddUrlGroup(new Uri(_cfg["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" })
.AddUrlGroup(new Uri(_cfg["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" })
.AddUrlGroup(new Uri(_cfg["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" });
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
@ -65,6 +79,7 @@ namespace OcelotApiGw
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var pathBase = _cfg["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
@ -75,6 +90,17 @@ namespace OcelotApiGw
app.UseDeveloperExceptionPage();
}
app.UseHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
loggerFactory.AddConsole(_cfg.GetSection("Logging"));
app.UseCors("CorsPolicy");

View File

@ -5,16 +5,19 @@ using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class BasketController : Controller
[ApiController]
public class BasketController : ControllerBase
{
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
public BasketController(ICatalogService catalogService, IBasketService basketService)
{
_catalog = catalogService;
@ -23,22 +26,24 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
[HttpPost]
[HttpPut]
public async Task<IActionResult> UpdateAllBasket([FromBody] UpdateBasketRequest data)
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BuyerId);
var currentBasket = await _basket.GetByIdAsync(data.BuyerId);
if (currentBasket == null)
{
currentBasket = new BasketData(data.BuyerId);
}
var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId));
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
var newBasket = new BasketData(data.BuyerId);
foreach (var bitem in data.Items)
@ -60,13 +65,16 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
});
}
await _basket.Update(newBasket);
return Ok(newBasket);
await _basket.UpdateAsync(newBasket);
return newBasket;
}
[HttpPut]
[Route("items")]
public async Task<IActionResult> UpdateQuantities([FromBody] UpdateBasketItemsRequest data)
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
@ -74,7 +82,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BasketId);
var currentBasket = await _basket.GetByIdAsync(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
@ -84,21 +92,26 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
foreach (var update in data.Updates)
{
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
if (basketItem == null)
{
return BadRequest($"Basket item with id {update.BasketItemId} not found");
}
basketItem.Quantity = update.NewQty;
}
// Save the updated basket
await _basket.Update(currentBasket);
return Ok(currentBasket);
await _basket.UpdateAsync(currentBasket);
return currentBasket;
}
[HttpPost]
[Route("items")]
public async Task<IActionResult> AddBasketItem([FromBody] AddBasketItemRequest data)
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
@ -106,12 +119,12 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
}
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItem(data.CatalogItemId);
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
var currentBasket = (await _basket.GetByIdAsync(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
@ -124,8 +137,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
});
// Step 4: Update basket
await _basket.Update(currentBasket);
await _basket.UpdateAsync(currentBasket);
return Ok();
}

View File

@ -1,19 +1,20 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class OrderController : Controller
[ApiController]
public class OrderController : ControllerBase
{
private readonly IBasketService _basketService;
private readonly IOrderApiClient _orderClient;
public OrderController(IBasketService basketService, IOrderApiClient orderClient)
{
_basketService = basketService;
@ -22,21 +23,23 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
[Route("draft/{basketId}")]
[HttpGet]
public async Task<IActionResult> GetOrderDraft(string basketId)
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetById(basketId);
var basket = await _basketService.GetByIdAsync(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket);
return Ok(orderDraft);
return await _orderClient.GetOrderDraftFromBasketAsync(basket);
}
}
}

View File

@ -1,8 +1,8 @@
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.1-sdk AS build
FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY . .
WORKDIR /src/src/ApiGateways/Mobile.Bff.Shopping/aggregator

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AssemblyName>Mobile.Shopping.HttpAggregator</AssemblyName>
<RootNamespace>Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator</RootNamespace>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
@ -12,11 +12,15 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.2.0-preview3-35497" />
</ItemGroup>
</Project>

View File

@ -22,7 +22,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
_urls = config.Value;
}
public async Task<BasketData> GetById(string id)
public async Task<BasketData> GetByIdAsync(string id)
{
var data = await _httpClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id));
@ -31,7 +31,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
return basket;
}
public async Task Update(BasketData currentBasket)
public async Task UpdateAsync(BasketData currentBasket)
{
var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json");

View File

@ -22,7 +22,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
_urls = config.Value;
}
public async Task<CatalogItem> GetCatalogItem(int id)
public async Task<CatalogItem> GetCatalogItemAsync(int id)
{
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id));
var catalogItem = JsonConvert.DeserializeObject<CatalogItem>(stringContent);
@ -30,7 +30,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
return catalogItem;
}
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids)
public async Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
{
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids));
var catalogItems = JsonConvert.DeserializeObject<CatalogItem[]>(stringContent);

View File

@ -1,15 +1,13 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public interface IBasketService
{
Task<BasketData> GetById(string id);
Task Update(BasketData currentBasket);
Task<BasketData> GetByIdAsync(string id);
Task UpdateAsync(BasketData currentBasket);
}
}

View File

@ -1,14 +1,13 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public interface ICatalogService
{
Task<CatalogItem> GetCatalogItem(int id);
Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids);
Task<CatalogItem> GetCatalogItemAsync(int id);
Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids);
}
}

View File

@ -1,13 +1,10 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public interface IOrderApiClient
{
Task<OrderData> GetOrderDraftFromBasket(BasketData basket);
Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket);
}
}

View File

@ -21,7 +21,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
_urls = config.Value;
}
public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket)
public async Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket)
{
var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure;
@ -16,6 +17,9 @@ using Microsoft.Extensions.Logging;
using Polly;
using Polly.Extensions.Http;
using Swashbuckle.AspNetCore.Swagger;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
@ -31,6 +35,16 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" })
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
.AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" })
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" })
.AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" });
services.AddCustomMvc(Configuration)
.AddCustomAuthentication(Configuration)
.AddHttpServices();
@ -47,15 +61,31 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
app.UsePathBase(pathBase);
}
app.UseHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
app.UseCors("CorsPolicy");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseMvc();
app.UseSwagger().UseSwaggerUI(c =>
@ -77,7 +107,8 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
services.AddOptions();
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
services.AddMvc();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSwaggerGen(options =>
{
@ -108,9 +139,10 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
@ -120,12 +152,14 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var identityUrl = configuration.GetValue<string>("urls:identity");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
})
.AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
@ -143,6 +177,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
return services;
}
public static IServiceCollection AddHttpServices(this IServiceCollection services)
{
//register delegating handlers
@ -163,8 +198,6 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
return services;
}
@ -174,7 +207,6 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
private static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()

View File

@ -5,16 +5,19 @@ using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class BasketController : Controller
[ApiController]
public class BasketController : ControllerBase
{
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
public BasketController(ICatalogService catalogService, IBasketService basketService)
{
_catalog = catalogService;
@ -23,22 +26,23 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
[HttpPost]
[HttpPut]
public async Task<IActionResult> UpdateAllBasket([FromBody] UpdateBasketRequest data)
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BuyerId);
var currentBasket = await _basket.GetByIdAsync(data.BuyerId);
if (currentBasket == null)
{
currentBasket = new BasketData(data.BuyerId);
}
var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId));
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
var newBasket = new BasketData(data.BuyerId);
foreach (var bitem in data.Items)
@ -60,13 +64,16 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
});
}
await _basket.Update(newBasket);
return Ok(newBasket);
await _basket.UpdateAsync(newBasket);
return newBasket;
}
[HttpPut]
[Route("items")]
public async Task<IActionResult> UpdateQuantities([FromBody] UpdateBasketItemsRequest data)
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
@ -74,7 +81,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BasketId);
var currentBasket = await _basket.GetByIdAsync(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
@ -92,13 +99,16 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
}
// Save the updated basket
await _basket.Update(currentBasket);
return Ok(currentBasket);
await _basket.UpdateAsync(currentBasket);
return currentBasket;
}
[HttpPost]
[Route("items")]
public async Task<IActionResult> AddBasketItem([FromBody] AddBasketItemRequest data)
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
@ -106,12 +116,12 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
}
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItem(data.CatalogItemId);
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
var currentBasket = (await _basket.GetByIdAsync(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
@ -124,8 +134,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
});
// Step 4: Update basket
await _basket.Update(currentBasket);
await _basket.UpdateAsync(currentBasket);
return Ok();
}

View File

@ -1,16 +1,19 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class OrderController : Controller
[ApiController]
public class OrderController : ControllerBase
{
private readonly IBasketService _basketService;
private readonly IOrderApiClient _orderClient;
@ -22,21 +25,23 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
[Route("draft/{basketId}")]
[HttpGet]
public async Task<IActionResult> GetOrderDraft(string basketId)
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetById(basketId);
var basket = await _basketService.GetByIdAsync(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket);
return Ok(orderDraft);
return await _orderClient.GetOrderDraftFromBasketAsync(basket);
}
}
}

View File

@ -1,8 +1,8 @@
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.1-sdk AS build
FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY . .
WORKDIR /src/src/ApiGateways/Web.Bff.Shopping/aggregator

View File

@ -10,7 +10,6 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public class BasketService : IBasketService
{
private readonly HttpClient _apiClient;
private readonly ILogger<BasketService> _logger;
private readonly UrlsConfig _urls;
@ -22,18 +21,19 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
_urls = config.Value;
}
public async Task<BasketData> GetById(string id)
public async Task<BasketData> GetByIdAsync(string id)
{
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id));
var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null;
return basket;
}
public async Task Update(BasketData currentBasket)
public async Task UpdateAsync(BasketData currentBasket)
{
var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json");
var data = await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent);
await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent);
}
}
}

View File

@ -11,7 +11,6 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public class CatalogService : ICatalogService
{
private readonly HttpClient _httpClient;
private readonly ILogger<CatalogService> _logger;
private readonly UrlsConfig _urls;
@ -23,20 +22,18 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
_urls = config.Value;
}
public async Task<CatalogItem> GetCatalogItem(int id)
public async Task<CatalogItem> GetCatalogItemAsync(int id)
{
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id));
var catalogItem = JsonConvert.DeserializeObject<CatalogItem>(stringContent);
return catalogItem;
return JsonConvert.DeserializeObject<CatalogItem>(stringContent);
}
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids)
public async Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
{
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids));
var catalogItems = JsonConvert.DeserializeObject<CatalogItem[]>(stringContent);
return catalogItems;
return JsonConvert.DeserializeObject<CatalogItem[]>(stringContent);
}
}
}

View File

@ -1,15 +1,12 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface IBasketService
{
Task<BasketData> GetById(string id);
Task Update(BasketData currentBasket);
Task<BasketData> GetByIdAsync(string id);
Task UpdateAsync(BasketData currentBasket);
}
}

View File

@ -1,14 +1,13 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface ICatalogService
{
Task<CatalogItem> GetCatalogItem(int id);
Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids);
Task<CatalogItem> GetCatalogItemAsync(int id);
Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids);
}
}

View File

@ -1,13 +1,10 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface IOrderApiClient
{
Task<OrderData> GetOrderDraftFromBasket(BasketData basket);
Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket);
}
}

View File

@ -10,7 +10,6 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public class OrderApiClient : IOrderApiClient
{
private readonly HttpClient _apiClient;
private readonly ILogger<OrderApiClient> _logger;
private readonly UrlsConfig _urls;
@ -22,7 +21,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
_urls = config.Value;
}
public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket)
public async Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket)
{
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");

View File

@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure;
@ -17,6 +18,9 @@ using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
{
@ -32,6 +36,16 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" })
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
.AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" })
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" })
.AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" });
services.AddCustomMvc(Configuration)
.AddCustomAuthentication(Configuration)
.AddApplicationServices();
@ -47,24 +61,39 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
app.UsePathBase(pathBase);
}
app.UseHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
app.UseCors("CorsPolicy");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseMvc();
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
//c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
});
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
//c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
});
}
}
@ -99,12 +128,14 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
return services;
}
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
services.AddMvc();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSwaggerGen(options =>
{
@ -135,7 +166,8 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
builder => builder
.SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
@ -177,6 +209,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AssemblyName>Web.Shopping.HttpAggregator</AssemblyName>
<RootNamespace>Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator</RootNamespace>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
@ -12,11 +12,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.2.0-preview3-35497" />
</ItemGroup>
</Project>

View File

@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="Autofac" Version="4.6.2" />
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0-preview3-35497" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="RabbitMQ.Client" Version="5.0.1" />

View File

@ -9,7 +9,7 @@
<PackageReference Include="Autofac" Version="4.6.2" />
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="3.0.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0-preview3-35497" />
</ItemGroup>
<ItemGroup>

View File

@ -6,10 +6,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.0-preview3-35497" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.0-preview3-35497" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.0-preview3-35497" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.0-preview3-35497" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>
<ItemGroup>

View File

@ -1,76 +0,0 @@
// 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.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.HealthChecks;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.HealthChecks
{
public class HealthCheckMiddleware
{
private readonly RequestDelegate _next;
private readonly string _path;
private readonly int? _port;
private readonly IHealthCheckService _service;
private readonly TimeSpan _timeout;
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port, TimeSpan timeout)
{
_port = port;
_service = service;
_next = next;
_timeout = timeout;
}
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path, TimeSpan timeout)
{
_path = path;
_service = service;
_next = next;
_timeout = timeout;
}
public async Task Invoke(HttpContext context)
{
if (IsHealthCheckRequest(context))
{
var timeoutTokenSource = new CancellationTokenSource(_timeout);
var result = await _service.CheckHealthAsync(timeoutTokenSource.Token);
var status = result.CheckStatus;
if (status != CheckStatus.Healthy)
context.Response.StatusCode = 503;
context.Response.Headers.Add("content-type", "application/json");
await context.Response.WriteAsync(JsonConvert.SerializeObject(new { status = status.ToString() }));
return;
}
else
{
await _next.Invoke(context);
}
}
private bool IsHealthCheckRequest(HttpContext context)
{
if (_port.HasValue)
{
var connInfo = context.Features.Get<IHttpConnectionFeature>();
if (connInfo.LocalPort == _port)
return true;
}
if (context.Request.Path == _path)
{
return true;
}
return false;
}
}
}

View File

@ -1,45 +0,0 @@
// 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.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNetCore.HealthChecks
{
public class HealthCheckStartupFilter : IStartupFilter
{
private string _path;
private int? _port;
private TimeSpan _timeout;
public HealthCheckStartupFilter(int port, TimeSpan timeout)
{
_port = port;
_timeout = timeout;
}
public HealthCheckStartupFilter(string path, TimeSpan timeout)
{
_path = path;
_timeout = timeout;
}
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
if (_port.HasValue)
{
app.UseMiddleware<HealthCheckMiddleware>(_port, _timeout);
}
else
{
app.UseMiddleware<HealthCheckMiddleware>(_path, _timeout);
}
next(app);
};
}
}
}

View File

@ -1,47 +0,0 @@
// 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.
using System;
using Microsoft.AspNetCore.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting
{
public static class HealthCheckWebHostBuilderExtension
{
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port)
=> UseHealthChecks(builder, port, DefaultTimeout);
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port, TimeSpan timeout)
{
Guard.ArgumentValid(port > 0 && port < 65536, nameof(port), "Port must be a value between 1 and 65535.");
Guard.ArgumentValid(timeout > TimeSpan.Zero, nameof(timeout), "Health check timeout must be a positive time span.");
builder.ConfigureServices(services =>
{
var existingUrl = builder.GetSetting(WebHostDefaults.ServerUrlsKey);
builder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};http://localhost:{port}");
services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(port, timeout));
});
return builder;
}
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path)
=> UseHealthChecks(builder, path, DefaultTimeout);
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path, TimeSpan timeout)
{
Guard.ArgumentNotNull(nameof(path), path);
// REVIEW: Is there a better URL path validator somewhere?
Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values.");
Guard.ArgumentValid(path.StartsWith("/"), nameof(path), "Path should start with '/'.");
Guard.ArgumentValid(timeout > TimeSpan.Zero, nameof(timeout), "Health check timeout must be a positive time span.");
builder.ConfigureServices(services => services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(path, timeout)));
return builder;
}
}
}

View File

@ -1,38 +0,0 @@
// 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.
using System;
using Microsoft.Extensions.HealthChecks;
namespace Microsoft.AspNetCore.Hosting
{
public static class HealthCheckWebHostExtensions
{
private const int DEFAULT_TIMEOUT_SECONDS = 300;
public static void RunWhenHealthy(this IWebHost webHost)
{
webHost.RunWhenHealthy(TimeSpan.FromSeconds(DEFAULT_TIMEOUT_SECONDS));
}
public static void RunWhenHealthy(this IWebHost webHost, TimeSpan timeout)
{
var healthChecks = webHost.Services.GetService(typeof(IHealthCheckService)) as IHealthCheckService;
var loops = 0;
do
{
var checkResult = healthChecks.CheckHealthAsync().Result;
if (checkResult.CheckStatus == CheckStatus.Healthy)
{
webHost.Run();
break;
}
System.Threading.Thread.Sleep(1000);
loops++;
} while (loops < timeout.TotalSeconds);
}
}
}

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.0" />
</ItemGroup>
</Project>

View File

@ -1,175 +0,0 @@
// 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.
using System;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
namespace Microsoft.Extensions.HealthChecks
{
// REVIEW: Do we want these to continue to use default parameters?
// REVIEW: What are the appropriate guards for these functions?
public static class AzureHealthCheckBuilderExtensions
{
public static HealthCheckBuilder AddAzureBlobStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string containerName = null, TimeSpan? cacheDuration = null)
{
var credentials = new StorageCredentials(accountName, accountKey);
var storageAccount = new CloudStorageAccount(credentials, true);
return AddAzureBlobStorageCheck(builder, storageAccount, containerName, cacheDuration);
}
public static HealthCheckBuilder AddAzureBlobStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string containerName = null, TimeSpan? cacheDuration = null)
{
builder.AddCheck($"AzureBlobStorageCheck {storageAccount.BlobStorageUri} {containerName}", async () =>
{
bool result;
try
{
var blobClient = storageAccount.CreateCloudBlobClient();
var properties = await blobClient.GetServicePropertiesAsync().ConfigureAwait(false);
if (!String.IsNullOrWhiteSpace(containerName))
{
var container = blobClient.GetContainerReference(containerName);
result = await container.ExistsAsync();
}
result = true;
}
catch (Exception)
{
result = false;
}
return result
? HealthCheckResult.Healthy($"AzureBlobStorage {storageAccount.BlobStorageUri} is available")
: HealthCheckResult.Unhealthy($"AzureBlobStorage {storageAccount.BlobStorageUri} is unavailable");
}, cacheDuration ?? builder.DefaultCacheDuration);
return builder;
}
public static HealthCheckBuilder AddAzureTableStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string tableName = null, TimeSpan? cacheDuration = null)
{
var credentials = new StorageCredentials(accountName, accountKey);
var storageAccount = new CloudStorageAccount(credentials, true);
return AddAzureTableStorageCheck(builder, storageAccount, tableName, cacheDuration);
}
public static HealthCheckBuilder AddAzureTableStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string tableName = null, TimeSpan? cacheDuration = null)
{
builder.AddCheck($"AzureTableStorageCheck {storageAccount.TableStorageUri} {tableName}", async () =>
{
bool result;
try
{
var tableClient = storageAccount.CreateCloudTableClient();
var properties = await tableClient.GetServicePropertiesAsync().ConfigureAwait(false);
if (!String.IsNullOrWhiteSpace(tableName))
{
var table = tableClient.GetTableReference(tableName);
result = await table.ExistsAsync();
}
result = true;
}
catch (Exception)
{
result = false;
}
return result
? HealthCheckResult.Healthy($"AzureTableStorage {storageAccount.BlobStorageUri} is available")
: HealthCheckResult.Unhealthy($"AzureTableStorage {storageAccount.BlobStorageUri} is unavailable");
}, cacheDuration ?? builder.DefaultCacheDuration);
return builder;
}
public static HealthCheckBuilder AddAzureFileStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string shareName = null, TimeSpan? cacheDuration = null)
{
var credentials = new StorageCredentials(accountName, accountKey);
var storageAccount = new CloudStorageAccount(credentials, true);
return AddAzureFileStorageCheck(builder, storageAccount, shareName, cacheDuration);
}
public static HealthCheckBuilder AddAzureFileStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string shareName = null, TimeSpan? cacheDuration = null)
{
builder.AddCheck($"AzureFileStorageCheck {storageAccount.FileStorageUri} {shareName}", async () =>
{
bool result;
try
{
var fileClient = storageAccount.CreateCloudFileClient();
var properties = await fileClient.GetServicePropertiesAsync().ConfigureAwait(false);
if (!String.IsNullOrWhiteSpace(shareName))
{
var share = fileClient.GetShareReference(shareName);
result = await share.ExistsAsync();
}
result = true;
}
catch (Exception)
{
result = false;
}
return result
? HealthCheckResult.Healthy($"AzureFileStorage {storageAccount.BlobStorageUri} is available")
: HealthCheckResult.Unhealthy($"AzureFileStorage {storageAccount.BlobStorageUri} is unavailable");
}, cacheDuration ?? builder.DefaultCacheDuration);
return builder;
}
public static HealthCheckBuilder AddAzureQueueStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string queueName = null, TimeSpan? cacheDuration = null)
{
var credentials = new StorageCredentials(accountName, accountKey);
var storageAccount = new CloudStorageAccount(credentials, true);
return AddAzureQueueStorageCheck(builder, storageAccount, queueName, cacheDuration);
}
public static HealthCheckBuilder AddAzureQueueStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string queueName = null, TimeSpan? cacheDuration = null)
{
builder.AddCheck($"AzureQueueStorageCheck {storageAccount.QueueStorageUri} {queueName}", async () =>
{
bool result;
try
{
var queueClient = storageAccount.CreateCloudQueueClient();
var properties = await queueClient.GetServicePropertiesAsync().ConfigureAwait(false);
if (!String.IsNullOrWhiteSpace(queueName))
{
var queue = queueClient.GetQueueReference(queueName);
result = await queue.ExistsAsync();
}
result = true;
}
catch (Exception)
{
result = false;
}
return result
? HealthCheckResult.Healthy($"AzureFileStorage {storageAccount.BlobStorageUri} is available")
: HealthCheckResult.Unhealthy($"AzureFileStorage {storageAccount.BlobStorageUri} is unavailable");
}, cacheDuration ?? builder.DefaultCacheDuration);
return builder;
}
}
}

View File

@ -1,26 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" />
<PackageReference Include="WindowsAzure.Storage" Version="9.1.0" />
</ItemGroup>
</Project>

View File

@ -1,22 +0,0 @@
// 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.
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HealthChecks.Azure")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0c4158b7-7153-4d2e-abfa-4ce07d44f75f")]

View File

@ -1,54 +0,0 @@
// 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.
using System;
using System.Data;
using System.Data.SqlClient;
namespace Microsoft.Extensions.HealthChecks
{
// REVIEW: What are the appropriate guards for these functions?
public static class HealthCheckBuilderSqlServerExtensions
{
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 () =>
{
try
{
//TODO: There is probably a much better way to do this.
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = "SELECT 1";
var result = (int)await command.ExecuteScalarAsync().ConfigureAwait(false);
if (result == 1)
{
return HealthCheckResult.Healthy($"SqlCheck({name}): Healthy");
}
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Unhealthy");
}
}
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}");
}
}, cacheDuration);
return builder;
}
}
}

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Data.SqlClient" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>
</Project>

View File

@ -1,109 +0,0 @@
// 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.
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Extensions.HealthChecks
{
public abstract class CachedHealthCheck
{
private static readonly TypeInfo HealthCheckTypeInfo = typeof(IHealthCheck).GetTypeInfo();
private volatile int _writerCount;
public CachedHealthCheck(string name, TimeSpan cacheDuration)
{
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
Guard.ArgumentValid(cacheDuration.TotalMilliseconds >= 0, nameof(cacheDuration), "Cache duration must be zero (disabled) or greater than zero.");
Name = name;
CacheDuration = cacheDuration;
}
public IHealthCheckResult CachedResult { get; internal set; }
public TimeSpan CacheDuration { get; }
public DateTimeOffset CacheExpiration { get; internal set; }
public string Name { get; }
protected virtual DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
protected abstract IHealthCheck Resolve(IServiceProvider serviceProvider);
public async ValueTask<IHealthCheckResult> RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken = default(CancellationToken))
{
while (CacheExpiration <= UtcNow)
{
// Can't use a standard lock here because of async, so we'll use this flag to determine when we should write a value,
// and the waiters who aren't allowed to write will just spin wait for the new value.
if (Interlocked.Exchange(ref _writerCount, 1) != 0)
{
await Task.Delay(5, cancellationToken).ConfigureAwait(false);
continue;
}
try
{
var check = Resolve(serviceProvider);
CachedResult = await check.CheckAsync(cancellationToken);
}
catch (OperationCanceledException)
{
CachedResult = HealthCheckResult.Unhealthy("The health check operation timed out");
}
catch (Exception ex)
{
CachedResult = HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}");
}
CacheExpiration = UtcNow + CacheDuration;
_writerCount = 0;
break;
}
return CachedResult;
}
public static CachedHealthCheck FromHealthCheck(string name, TimeSpan cacheDuration, IHealthCheck healthCheck)
{
Guard.ArgumentNotNull(nameof(healthCheck), healthCheck);
return new TypeOrHealthCheck_HealthCheck(name, cacheDuration, healthCheck);
}
public static CachedHealthCheck FromType(string name, TimeSpan cacheDuration, Type healthCheckType)
{
Guard.ArgumentNotNull(nameof(healthCheckType), healthCheckType);
Guard.ArgumentValid(HealthCheckTypeInfo.IsAssignableFrom(healthCheckType.GetTypeInfo()), nameof(healthCheckType), $"Health check must implement '{typeof(IHealthCheck).FullName}'.");
return new TypeOrHealthCheck_Type(name, cacheDuration, healthCheckType);
}
class TypeOrHealthCheck_HealthCheck : CachedHealthCheck
{
private readonly IHealthCheck _healthCheck;
public TypeOrHealthCheck_HealthCheck(string name, TimeSpan cacheDuration, IHealthCheck healthCheck) : base(name, cacheDuration)
=> _healthCheck = healthCheck;
protected override IHealthCheck Resolve(IServiceProvider serviceProvider) => _healthCheck;
}
class TypeOrHealthCheck_Type : CachedHealthCheck
{
private readonly Type _healthCheckType;
public TypeOrHealthCheck_Type(string name, TimeSpan cacheDuration, Type healthCheckType) : base(name, cacheDuration)
=> _healthCheckType = healthCheckType;
protected override IHealthCheck Resolve(IServiceProvider serviceProvider)
=> (IHealthCheck)serviceProvider.GetRequiredService(_healthCheckType);
}
}
}

View File

@ -1,19 +0,0 @@
// 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.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public static class CachedHealthCheckExtensions
{
public static ValueTask<IHealthCheckResult> RunAsync(this CachedHealthCheck check, IServiceProvider serviceProvider)
{
Guard.ArgumentNotNull(nameof(check), check);
return check.RunAsync(serviceProvider, CancellationToken.None);
}
}
}

View File

@ -1,13 +0,0 @@
// 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.
namespace Microsoft.Extensions.HealthChecks
{
public enum CheckStatus
{
Unknown,
Unhealthy,
Healthy,
Warning
}
}

View File

@ -1,116 +0,0 @@
// 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.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public static partial class HealthCheckBuilderExtensions
{
// Lambda versions of AddCheck for Func/Func<Task>/Func<ValueTask>
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration);
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration);
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration);
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration);
}
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration);
}
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration);
}
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration);
}
// IHealthCheck versions of AddCheck
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string checkName, IHealthCheck check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck(checkName, check, builder.DefaultCacheDuration);
}
// Type versions of AddCheck
public static HealthCheckBuilder AddCheck<TCheck>(this HealthCheckBuilder builder, string name) where TCheck : class, IHealthCheck
{
Guard.ArgumentNotNull(nameof(builder), builder);
return builder.AddCheck<TCheck>(name, builder.DefaultCacheDuration);
}
}
}

View File

@ -1,69 +0,0 @@
// 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.
using System;
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public static partial class HealthCheckBuilderExtensions
{
// Numeric checks
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>
{
Guard.ArgumentNotNull(nameof(builder), builder);
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
builder.AddCheck(name, () =>
{
var currentValue = currentValueFunc();
var status = currentValue.CompareTo(minValue) >= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
return HealthCheckResult.FromStatus(
status,
$"min={minValue}, current={currentValue}",
new Dictionary<string, object> { { "min", minValue }, { "current", currentValue } }
);
}, cacheDuration);
return builder;
}
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>
{
Guard.ArgumentNotNull(nameof(builder), builder);
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
builder.AddCheck(name, () =>
{
var currentValue = currentValueFunc();
var status = currentValue.CompareTo(maxValue) <= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
return HealthCheckResult.FromStatus(
status,
$"max={maxValue}, current={currentValue}",
new Dictionary<string, object> { { "max", maxValue }, { "current", currentValue } }
);
}, cacheDuration);
return builder;
}
}
}

View File

@ -1,31 +0,0 @@
// 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.
using System;
using System.Diagnostics;
namespace Microsoft.Extensions.HealthChecks
{
public static partial class HealthCheckBuilderExtensions
{
// System checks
public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize)
=> 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)
=> 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)
=> 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);
}
}

View File

@ -1,83 +0,0 @@
// 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.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.HealthChecks.Internal;
namespace Microsoft.Extensions.HealthChecks
{
public static partial class HealthCheckBuilderExtensions
{
// Default URL check
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url)
{
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,
Func<HttpResponseMessage, IHealthCheckResult> checkFunc,
TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
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,
Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc,
TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
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,
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc,
TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
Guard.ArgumentNotNullOrEmpty(nameof(url), url);
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
var urlCheck = new UrlChecker(checkFunc, url);
builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync(), cacheDuration);
return builder;
}
}
}

View File

@ -1,86 +0,0 @@
// 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.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Extensions.HealthChecks
{
/// <summary>
/// Represents a composite health check result built from several results.
/// </summary>
public class CompositeHealthCheckResult : IHealthCheckResult
{
private static readonly IReadOnlyDictionary<string, object> _emptyData = new Dictionary<string, object>();
private readonly CheckStatus _initialStatus;
private readonly CheckStatus _partiallyHealthyStatus;
private readonly Dictionary<string, IHealthCheckResult> _results = new Dictionary<string, IHealthCheckResult>(StringComparer.OrdinalIgnoreCase);
public CompositeHealthCheckResult(CheckStatus partiallyHealthyStatus = CheckStatus.Warning,
CheckStatus initialStatus = CheckStatus.Unknown)
{
_partiallyHealthyStatus = partiallyHealthyStatus;
_initialStatus = initialStatus;
}
public CheckStatus CheckStatus
{
get
{
var checkStatuses = new HashSet<CheckStatus>(_results.Select(x => x.Value.CheckStatus));
if (checkStatuses.Count == 0)
{
return _initialStatus;
}
if (checkStatuses.Count == 1)
{
return checkStatuses.First();
}
if (checkStatuses.Contains(CheckStatus.Healthy))
{
return _partiallyHealthyStatus;
}
return CheckStatus.Unhealthy;
}
}
public string Description => string.Join(Environment.NewLine, _results.Select(r => $"{r.Key}: {r.Value.Description}"));
public IReadOnlyDictionary<string, object> Data
{
get
{
var result = new Dictionary<string, object>();
foreach (var kvp in _results)
result.Add(kvp.Key, kvp.Value.Data);
return result;
}
}
public IReadOnlyDictionary<string, IHealthCheckResult> Results => _results;
public void Add(string name, CheckStatus status, string description)
=> Add(name, status, description, null);
public void Add(string name, CheckStatus status, string description, Dictionary<string, object> data)
{
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
Guard.ArgumentValid(status != CheckStatus.Unknown, nameof(status), "Cannot add 'Unknown' status to composite health check result.");
Guard.ArgumentNotNullOrEmpty(nameof(description), description);
_results.Add(name, HealthCheckResult.FromStatus(status, description, data));
}
public void Add(string name, IHealthCheckResult checkResult)
{
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
Guard.ArgumentNotNull(nameof(checkResult), checkResult);
_results.Add(name, checkResult);
}
}
}

View File

@ -1,42 +0,0 @@
// 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.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheck : IHealthCheck
{
protected HealthCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(check), check);
Check = check;
}
protected Func<CancellationToken, ValueTask<IHealthCheckResult>> Check { get; }
public ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken))
=> Check(cancellationToken);
public static HealthCheck FromCheck(Func<IHealthCheckResult> check)
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()));
public static HealthCheck FromCheck(Func<CancellationToken, IHealthCheckResult> check)
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)));
public static HealthCheck FromTaskCheck(Func<Task<IHealthCheckResult>> check)
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()));
public static HealthCheck FromTaskCheck(Func<CancellationToken, Task<IHealthCheckResult>> check)
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)));
public static HealthCheck FromValueTaskCheck(Func<ValueTask<IHealthCheckResult>> check)
=> new HealthCheck(token => check());
public static HealthCheck FromValueTaskCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
=> new HealthCheck(check);
}
}

View File

@ -1,135 +0,0 @@
// 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.
using System;
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckBuilder
{
private readonly Dictionary<string, CachedHealthCheck> _checksByName;
private readonly HealthCheckGroup _currentGroup;
private readonly Dictionary<string, HealthCheckGroup> _groups;
public HealthCheckBuilder()
{
_checksByName = new Dictionary<string, CachedHealthCheck>(StringComparer.OrdinalIgnoreCase);
_currentGroup = new HealthCheckGroup(string.Empty, CheckStatus.Unhealthy);
_groups = new Dictionary<string, HealthCheckGroup>(StringComparer.OrdinalIgnoreCase)
{
[string.Empty] = _currentGroup
};
DefaultCacheDuration = TimeSpan.FromMinutes(5);
}
/// <summary>
/// This constructor should only be used when creating a grouped health check builder.
/// </summary>
public HealthCheckBuilder(HealthCheckBuilder rootBuilder, HealthCheckGroup currentGroup)
{
Guard.ArgumentNotNull(nameof(rootBuilder), rootBuilder);
Guard.ArgumentNotNull(nameof(currentGroup), currentGroup);
_checksByName = rootBuilder._checksByName;
_currentGroup = currentGroup;
_groups = rootBuilder._groups;
DefaultCacheDuration = rootBuilder.DefaultCacheDuration;
}
/// <summary>
/// Gets the registered checks, indexed by check name.
/// </summary>
public IReadOnlyDictionary<string, CachedHealthCheck> ChecksByName => _checksByName;
/// <summary>
/// Gets the current default cache duration used when registering checks.
/// </summary>
public TimeSpan DefaultCacheDuration { get; private set; }
/// <summary>
/// Gets the registered groups, indexed by group name. The root group's name is <see cref="string.Empty"/>.
/// </summary>
public IReadOnlyDictionary<string, HealthCheckGroup> Groups => _groups;
/// <summary>
/// Registers a health check type that will later be resolved via dependency
/// injection.
/// </summary>
public HealthCheckBuilder AddCheck<TCheck>(string checkName, TimeSpan cacheDuration) where TCheck : class, IHealthCheck
{
Guard.ArgumentNotNullOrEmpty(nameof(checkName), checkName);
Guard.ArgumentValid(!_checksByName.ContainsKey(checkName), nameof(checkName), $"A check with name '{checkName}' has already been registered.");
var namedCheck = CachedHealthCheck.FromType(checkName, cacheDuration, typeof(TCheck));
_checksByName.Add(checkName, namedCheck);
_currentGroup.ChecksInternal.Add(namedCheck);
return this;
}
/// <summary>
/// Registers a concrete health check to the builder.
/// </summary>
public HealthCheckBuilder AddCheck(string checkName, IHealthCheck check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNullOrEmpty(nameof(checkName), checkName);
Guard.ArgumentNotNull(nameof(check), check);
Guard.ArgumentValid(!_checksByName.ContainsKey(checkName), nameof(checkName), $"A check with name '{checkName}' has already been registered.");
var namedCheck = CachedHealthCheck.FromHealthCheck(checkName, cacheDuration, check);
_checksByName.Add(checkName, namedCheck);
_currentGroup.ChecksInternal.Add(namedCheck);
return this;
}
/// <summary>
/// Creates a new health check group, to which you can add one or more health
/// checks. Uses <see cref="CheckStatus.Unhealthy"/> when the group is
/// partially successful.
/// </summary>
public HealthCheckBuilder AddHealthCheckGroup(string groupName, Action<HealthCheckBuilder> groupChecks)
=> AddHealthCheckGroup(groupName, groupChecks, CheckStatus.Unhealthy);
/// <summary>
/// Creates a new health check group, to which you can add one or more health
/// checks.
/// </summary>
public HealthCheckBuilder AddHealthCheckGroup(string groupName, Action<HealthCheckBuilder> groupChecks, CheckStatus partialSuccessStatus)
{
Guard.ArgumentNotNullOrEmpty(nameof(groupName), groupName);
Guard.ArgumentNotNull(nameof(groupChecks), groupChecks);
Guard.ArgumentValid(partialSuccessStatus != CheckStatus.Unknown, nameof(partialSuccessStatus), "Check status 'Unknown' is not valid for partial success.");
Guard.ArgumentValid(!_groups.ContainsKey(groupName), nameof(groupName), $"A group with name '{groupName}' has already been registered.");
Guard.OperationValid(_currentGroup.GroupName == string.Empty, "Nested groups are not supported by HealthCheckBuilder.");
var group = new HealthCheckGroup(groupName, partialSuccessStatus);
_groups.Add(groupName, group);
var innerBuilder = new HealthCheckBuilder(this, group);
groupChecks(innerBuilder);
return this;
}
public HealthCheckBuilder WithDefaultCacheDuration(TimeSpan duration)
{
Guard.ArgumentValid(duration >= TimeSpan.Zero, nameof(duration), "Duration must be zero (disabled) or a positive duration.");
DefaultCacheDuration = duration;
return this;
}
public HealthCheckBuilder WithPartialSuccessStatus(CheckStatus partiallyHealthyStatus)
{
_currentGroup.PartiallyHealthyStatus = partiallyHealthyStatus;
return this;
}
}
}

View File

@ -1,37 +0,0 @@
// 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.
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckGroup
{
private CheckStatus _partialSuccessStatus;
public HealthCheckGroup(string groupName, CheckStatus partialSuccessStatus)
{
Guard.ArgumentNotNull(nameof(groupName), groupName);
GroupName = groupName;
PartiallyHealthyStatus = partialSuccessStatus;
}
public IReadOnlyList<CachedHealthCheck> Checks => ChecksInternal.AsReadOnly();
internal List<CachedHealthCheck> ChecksInternal { get; } = new List<CachedHealthCheck>();
public string GroupName { get; }
public CheckStatus PartiallyHealthyStatus
{
get => _partialSuccessStatus;
internal set
{
Guard.ArgumentValid(value != CheckStatus.Unknown, nameof(value), "Check status 'Unknown' is not valid for partial success.");
_partialSuccessStatus = value;
}
}
}
}

View File

@ -1,53 +0,0 @@
// 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.
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckResult : IHealthCheckResult
{
private static readonly IReadOnlyDictionary<string, object> _emptyData = new Dictionary<string, object>();
public CheckStatus CheckStatus { get; }
public IReadOnlyDictionary<string, object> Data { get; }
public string Description { get; }
private HealthCheckResult(CheckStatus checkStatus, string description, IReadOnlyDictionary<string, object> data)
{
CheckStatus = checkStatus;
Description = description;
Data = data ?? _emptyData;
}
public static HealthCheckResult Unhealthy(string description)
=> new HealthCheckResult(CheckStatus.Unhealthy, description, null);
public static HealthCheckResult Unhealthy(string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(CheckStatus.Unhealthy, description, data);
public static HealthCheckResult Healthy(string description)
=> new HealthCheckResult(CheckStatus.Healthy, description, null);
public static HealthCheckResult Healthy(string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(CheckStatus.Healthy, description, data);
public static HealthCheckResult Warning(string description)
=> new HealthCheckResult(CheckStatus.Warning, description, null);
public static HealthCheckResult Warning(string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(CheckStatus.Warning, description, data);
public static HealthCheckResult Unknown(string description)
=> new HealthCheckResult(CheckStatus.Unknown, description, null);
public static HealthCheckResult Unknown(string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(CheckStatus.Unknown, description, data);
public static HealthCheckResult FromStatus(CheckStatus status, string description)
=> new HealthCheckResult(status, description, null);
public static HealthCheckResult FromStatus(CheckStatus status, string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(status, description, data);
}
}

View File

@ -1,12 +0,0 @@
// 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.
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckResults
{
public IList<IHealthCheckResult> CheckResults { get; } = new List<IHealthCheckResult>();
}
}

View File

@ -1,119 +0,0 @@
// 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.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckService : IHealthCheckService
{
private readonly HealthCheckBuilder _builder;
private readonly IReadOnlyList<HealthCheckGroup> _groups;
private readonly HealthCheckGroup _root;
private readonly IServiceProvider _serviceProvider;
private readonly IServiceScopeFactory _serviceScopeFactory;
public HealthCheckService(HealthCheckBuilder builder, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory)
{
_builder = builder;
_groups = GetGroups().Where(group => group.GroupName != string.Empty).ToList();
_root = GetGroup(string.Empty);
_serviceProvider = serviceProvider;
_serviceScopeFactory = serviceScopeFactory;
}
public async Task<CompositeHealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken))
{
using (var scope = GetServiceScope())
{
var scopeServiceProvider = scope.ServiceProvider;
var groupTasks = _groups.Select(group => new { Group = group, Task = RunGroupAsync(scopeServiceProvider, group, cancellationToken) }).ToList();
var result = await RunGroupAsync(scopeServiceProvider, _root, cancellationToken).ConfigureAwait(false);
await Task.WhenAll(groupTasks.Select(x => x.Task));
foreach (var groupTask in groupTasks)
{
result.Add($"Group({groupTask.Group.GroupName})", groupTask.Task.Result);
}
return result;
}
}
public IReadOnlyList<CachedHealthCheck> GetAllChecks()
=> _builder.ChecksByName.Values.ToList().AsReadOnly();
public CachedHealthCheck GetCheck(string checkName)
=> _builder.ChecksByName[checkName];
public HealthCheckGroup GetGroup(string groupName)
=> _builder.Groups[groupName];
public IReadOnlyList<HealthCheckGroup> GetGroups()
=> _builder.Groups.Values.ToList().AsReadOnly();
private IServiceScope GetServiceScope()
=> _serviceScopeFactory == null ? new UnscopedServiceProvider(_serviceProvider) : _serviceScopeFactory.CreateScope();
public async ValueTask<IHealthCheckResult> RunCheckAsync(CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken))
{
using (var scope = GetServiceScope())
{
return await RunCheckAsync(scope.ServiceProvider, healthCheck, cancellationToken).ConfigureAwait(false);
}
}
/// <summary>
/// Uses the provided service provider and executes the provided check.
/// </summary>
public ValueTask<IHealthCheckResult> RunCheckAsync(IServiceProvider serviceProvider, CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken))
{
Guard.ArgumentNotNull(nameof(serviceProvider), serviceProvider);
Guard.ArgumentNotNull(nameof(healthCheck), healthCheck);
return healthCheck.RunAsync(serviceProvider, cancellationToken);
}
/// <summary>
/// Creates a new resolution scope from the default service provider and executes the checks in the given group.
/// </summary>
public async Task<CompositeHealthCheckResult> RunGroupAsync(HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken))
{
using (var scope = GetServiceScope())
return await RunGroupAsync(scope.ServiceProvider, group, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Uses the provided service provider and executes the checks in the given group.
/// </summary>
public async Task<CompositeHealthCheckResult> RunGroupAsync(IServiceProvider serviceProvider, HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken))
{
var result = new CompositeHealthCheckResult(group.PartiallyHealthyStatus);
var checkTasks = group.Checks.Select(check => new { Check = check, Task = check.RunAsync(serviceProvider, cancellationToken).AsTask() }).ToList();
await Task.WhenAll(checkTasks.Select(checkTask => checkTask.Task));
foreach (var checkTask in checkTasks)
{
result.Add(checkTask.Check.Name, checkTask.Task.Result);
}
return result;
}
private class UnscopedServiceProvider : IServiceScope
{
public UnscopedServiceProvider(IServiceProvider serviceProvider)
=> ServiceProvider = serviceProvider;
public IServiceProvider ServiceProvider { get; }
public void Dispose() { }
}
}
}

View File

@ -1,30 +0,0 @@
// 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.
using System;
using System.Linq;
using Microsoft.Extensions.HealthChecks;
namespace Microsoft.Extensions.DependencyInjection
{
public static class HealthCheckServiceCollectionExtensions
{
private static readonly Type HealthCheckServiceInterface = typeof(IHealthCheckService);
public static IServiceCollection AddHealthChecks(this IServiceCollection services, Action<HealthCheckBuilder> checks)
{
Guard.OperationValid(!services.Any(descriptor => descriptor.ServiceType == HealthCheckServiceInterface), "AddHealthChecks may only be called once.");
var builder = new HealthCheckBuilder();
services.AddSingleton<IHealthCheckService, HealthCheckService>(serviceProvider =>
{
var serviceScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
return new HealthCheckService(builder, serviceProvider, serviceScopeFactory);
});
checks(builder);
return services;
}
}
}

View File

@ -1,13 +0,0 @@
// 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.
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public interface IHealthCheck
{
ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -1,14 +0,0 @@
// 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.
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public interface IHealthCheckResult
{
CheckStatus CheckStatus { get; }
string Description { get; }
IReadOnlyDictionary<string, object> Data { get; }
}
}

View File

@ -1,58 +0,0 @@
// 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.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public interface IHealthCheckService
{
/// <summary>
/// Runs all registered health checks.
/// </summary>
Task<CompositeHealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Gets all registered health checks as a flat list.
/// </summary>
IReadOnlyList<CachedHealthCheck> GetAllChecks();
/// <summary>
/// Gets a health check by name.
/// </summary>
CachedHealthCheck GetCheck(string checkName);
/// <summary>
/// Gets all health checks in a group.
/// </summary>
HealthCheckGroup GetGroup(string groupName);
/// <summary>
/// Gets all the health check groups.
/// </summary>
IReadOnlyList<HealthCheckGroup> GetGroups();
/// <summary>
/// Creates a new resolution scope from the default service provider and executes the provided check.
/// </summary>
ValueTask<IHealthCheckResult> RunCheckAsync(CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Uses the provided service provider and executes the provided check.
/// </summary>
ValueTask<IHealthCheckResult> RunCheckAsync(IServiceProvider serviceProvider, CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Creates a new resolution scope from the default service provider and executes the checks in the given group.
/// </summary>
Task<CompositeHealthCheckResult> RunGroupAsync(HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Uses the provided service provider and executes the checks in the given group.
/// </summary>
Task<CompositeHealthCheckResult> RunGroupAsync(IServiceProvider serviceProvider, HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -1,68 +0,0 @@
// 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.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks.Internal
{
public class UrlChecker
{
private readonly Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> _checkFunc;
private readonly string _url;
public UrlChecker(Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc, string url)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
Guard.ArgumentNotNullOrEmpty(nameof(url), url);
_checkFunc = checkFunc;
_url = url;
}
public CheckStatus PartiallyHealthyStatus { get; set; } = CheckStatus.Warning;
public async Task<IHealthCheckResult> CheckAsync()
{
using (var httpClient = CreateHttpClient())
{
try
{
var response = await httpClient.GetAsync(_url).ConfigureAwait(false);
return await _checkFunc(response);
}
catch (Exception ex)
{
var data = new Dictionary<string, object> { { "url", _url } };
return HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}", data);
}
}
}
private HttpClient CreateHttpClient()
{
var httpClient = GetHttpClient();
httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true };
return httpClient;
}
public static async ValueTask<IHealthCheckResult> DefaultUrlCheck(HttpResponseMessage response)
{
var status = response.IsSuccessStatusCode ? CheckStatus.Healthy : CheckStatus.Unhealthy;
var data = new Dictionary<string, object>
{
{ "url", response.RequestMessage.RequestUri.ToString() },
{ "status", (int)response.StatusCode },
{ "reason", response.ReasonPhrase },
{ "body", await response.Content?.ReadAsStringAsync() }
};
return HealthCheckResult.FromStatus(status, $"status code {response.StatusCode} ({(int)response.StatusCode})", data);
}
protected virtual HttpClient GetHttpClient()
=> new HttpClient();
}
}

View File

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" />
</ItemGroup>
</Project>

View File

@ -1,57 +0,0 @@
// 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.
using System;
using System.Collections.Generic;
static class Guard
{
public static void ArgumentNotNull(string argumentName, object value)
{
if (value == null)
{
throw new ArgumentNullException(argumentName);
}
}
public static void ArgumentNotNullOrEmpty(string argumentName, string value)
{
if (value == null)
{
throw new ArgumentNullException(argumentName);
}
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("Value cannot be an empty string.", argumentName);
}
}
// Use IReadOnlyCollection<T> instead of IEnumerable<T> to discourage double enumeration
public static void ArgumentNotNullOrEmpty<T>(string argumentName, IReadOnlyCollection<T> items)
{
if (items == null)
{
throw new ArgumentNullException(argumentName);
}
if (items.Count == 0)
{
throw new ArgumentException("Collection must contain at least one item.", argumentName);
}
}
public static void ArgumentValid(bool valid, string argumentName, string exceptionMessage)
{
if (!valid)
{
throw new ArgumentException(exceptionMessage, argumentName);
}
}
public static void OperationValid(bool valid, string exceptionMessage)
{
if (!valid)
{
throw new InvalidOperationException(exceptionMessage);
}
}
}

View File

@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0-preview3-35497" />
</ItemGroup>
</Project>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup>
@ -13,26 +13,31 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" />
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.6" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>
</Project>

View File

@ -13,54 +13,44 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class BasketController : Controller
[ApiController]
public class BasketController : ControllerBase
{
private readonly IBasketRepository _repository;
private readonly IIdentityService _identitySvc;
private readonly IIdentityService _identityService;
private readonly IEventBus _eventBus;
public BasketController(IBasketRepository repository,
IIdentityService identityService,
IEventBus eventBus)
public BasketController(IBasketRepository repository, IIdentityService identityService, IEventBus eventBus)
{
_repository = repository;
_identitySvc = identityService;
_identityService = identityService;
_eventBus = eventBus;
}
// GET /id
[HttpGet("{id}")]
[ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Get(string id)
public async Task<ActionResult<CustomerBasket>> GetBasketByIdAsync(string id)
{
var basket = await _repository.GetBasketAsync(id);
if (basket == null)
{
return Ok(new CustomerBasket(id) { });
}
return Ok(basket);
return basket ?? new CustomerBasket(id);
}
// POST /value
[HttpPost]
[ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Post([FromBody]CustomerBasket value)
public async Task<ActionResult<CustomerBasket>> UpdateBasketAsync([FromBody]CustomerBasket value)
{
var basket = await _repository.UpdateBasketAsync(value);
return Ok(basket);
return await _repository.UpdateBasketAsync(value);
}
[Route("checkout")]
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.Accepted)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Checkout([FromBody]BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId)
public async Task<ActionResult> CheckoutAsync([FromBody]BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId)
{
var userId = _identitySvc.GetUserIdentity();
var userId = _identityService.GetUserIdentity();
basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ?
guid : basketCheckout.RequestId;
@ -80,17 +70,17 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
// Once basket is checkout, sends an integration event to
// ordering.api to convert basket to order and proceeds with
// order creation process
_eventBus.Publish(eventMessage);
_eventBus.Publish(eventMessage);
return Accepted();
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(string id)
[ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)]
public async Task DeleteBasketByIdAsync(string id)
{
_repository.DeleteBasketAsync(id);
await _repository.DeleteBasketAsync(id);
}
}
}

View File

@ -1,8 +1,8 @@
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.1-sdk AS build
FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY . .
WORKDIR /src/src/Services/Basket/Basket.API

View File

@ -10,7 +10,6 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
public class RedisBasketRepository : IBasketRepository
{
private readonly ILogger<RedisBasketRepository> _logger;
private readonly ConnectionMultiplexer _redis;
private readonly IDatabase _database;
@ -30,12 +29,14 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
{
var server = GetServer();
var data = server.Keys();
return data?.Select(k => k.ToString());
}
public async Task<CustomerBasket> GetBasketAsync(string customerId)
{
var data = await _database.StringGetAsync(customerId);
if (data.IsNullOrEmpty)
{
return null;
@ -47,6 +48,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
public async Task<CustomerBasket> UpdateBasketAsync(CustomerBasket basket)
{
var created = await _database.StringSetAsync(basket.BuyerId, JsonConvert.SerializeObject(basket));
if (!created)
{
_logger.LogInformation("Problem occur persisting the item.");

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