Merge branch 'master' into order-processflow-redesign
# Conflicts: # eShopOnContainers-ServicesAndWebApps.sln
This commit is contained in:
commit
ed641df444
15
README.md
15
README.md
@ -1,12 +1,13 @@
|
|||||||
# eShopOnContainers - Microservices Architecture and Containers based Reference Application (**ALPHA state** - VS 2017 and CLI environments compatible)
|
# eShopOnContainers - Microservices Architecture and Containers based Reference Application (**BETA state** - Visual Studio 2017 and CLI environments compatible)
|
||||||
Sample .NET Core reference application, powered by Microsoft, based on a simplified microservices architecture and Docker containers. <p>
|
Sample .NET Core reference application, powered by Microsoft, based on a simplified microservices architecture and Docker containers. <p>
|
||||||
|
|
||||||
> ### DISCLAIMER
|
> ### DISCLAIMER
|
||||||
> **IMPORTANT:** The current state of this sample application is **ALPHA**, consider it version a 0.1 foundational version, therefore, many areas could be improved and change significantly while refactoring current code and implementing new features. **Feedback with improvements and pull requests from the community will be highly appreciated and accepted.**
|
> **IMPORTANT:** The current state of this sample application is **BETA**, consider it version a 0.1 foundational version, therefore, many areas could be improved and change significantly while refactoring current code and implementing new features. **Feedback with improvements and pull requests from the community will be highly appreciated and accepted.**
|
||||||
>
|
>
|
||||||
> This reference application proposes a simplified microservice oriented architecture implementation to introduce technologies like .NET Core with Docker containers through a comprehensive application. However, this reference application it is not trying to solve all the problems in a large and mission-critical distributed system, it is just a bootstrap for developers to easily get started in the world of Docker containers and microservices with .NET Core.
|
> This reference application proposes a simplified microservice oriented architecture implementation to introduce technologies like .NET Core with Docker containers through a comprehensive application. The chosen domain is an eShop/eCommerce but simply because it is a well-know domain by most people/developers.
|
||||||
|
However, this sample application should not be considered as an "eCommerce reference model", at all. The implemented business domain might not be ideal from an eCommerce business point of view. It is neither trying to solve all the problems in a large, scalable and mission-critical distributed system. It is just a bootstrap for developers to easily get started in the world of Docker containers and microservices with .NET Core.
|
||||||
> <p>For example, the next step (still not covered in eShopOnContainers) after understanding Docker containers and microservices development with .NET Core, is to select a microservice cluster/orchestrator like Docker Swarm, Kubernetes or DC/OS (in Azure Container Service) or Azure Service Fabric which in most of the cases will require additional partial changes to your application's configuration (although the present architecture should work on most orchestrators with small changes).
|
> <p>For example, the next step (still not covered in eShopOnContainers) after understanding Docker containers and microservices development with .NET Core, is to select a microservice cluster/orchestrator like Docker Swarm, Kubernetes or DC/OS (in Azure Container Service) or Azure Service Fabric which in most of the cases will require additional partial changes to your application's configuration (although the present architecture should work on most orchestrators with small changes).
|
||||||
> Or moving your databases to HA cloud services, or implementing your EventBus with Azure Service Bus or any other production ready Service Bus in the market.
|
> Additional steps would be to move your databases to HA cloud services, or to implement your EventBus with Azure Service Bus or any other production ready Service Bus in the market.
|
||||||
> <p> In the future we might fork this project and make multiple versions targeting specific microservice cluster/orchestrators plus using additional cloud infrastructure. <p>
|
> <p> In the future we might fork this project and make multiple versions targeting specific microservice cluster/orchestrators plus using additional cloud infrastructure. <p>
|
||||||
> <img src="img/exploring-to-production-ready.png">
|
> <img src="img/exploring-to-production-ready.png">
|
||||||
> Read the planned <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>Roadmap and Milestones for future releases of eShopOnContainers</a> within the Wiki for further info about possible new implementations and provide feedback at the <a href='https://github.com/dotnet/eShopOnContainers/issues'>ISSUES section</a> if you'd like to see any specific scenario implemented or improved. Also, feel free to discuss on any current issue.
|
> Read the planned <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>Roadmap and Milestones for future releases of eShopOnContainers</a> within the Wiki for further info about possible new implementations and provide feedback at the <a href='https://github.com/dotnet/eShopOnContainers/issues'>ISSUES section</a> if you'd like to see any specific scenario implemented or improved. Also, feel free to discuss on any current issue.
|
||||||
@ -32,7 +33,7 @@ Additional miroservice styles with other frameworks and No-SQL databases will be
|
|||||||
> <p> However, in a real production environment it is recommended to have your databases (SQL Server and Redis, in this case) in HA (High Available) services like Azure SQL Database, Redis as a service or any other clustering system. If you want to change to a production configuration, you'll just need to change the connection strings once you have set up the servers in a HA cloud or on-premises.
|
> <p> However, in a real production environment it is recommended to have your databases (SQL Server and Redis, in this case) in HA (High Available) services like Azure SQL Database, Redis as a service or any other clustering system. If you want to change to a production configuration, you'll just need to change the connection strings once you have set up the servers in a HA cloud or on-premises.
|
||||||
|
|
||||||
## Related documentation and guidance
|
## Related documentation and guidance
|
||||||
While developing this reference application, we are creating a reference Guide/eBook named <b>"Architecting and Developing Containerized and Microservice based .NET Applications"</b> which explains in detail how to develop this kind of architectural style (microservices, Docker containers, Domain-Driven Design for certain microservices) plus other simpler architectural styles, like monolithic apps that can also live as Docker containers.
|
While developing this reference application, we've been creating a reference <b>Guide/eBook</b> focusing on <b>architecting and developing containerized and microservice based .NET Applications</b> (download link available below) which explains in detail how to develop this kind of architectural style (microservices, Docker containers, Domain-Driven Design for certain microservices) plus other simpler architectural styles, like monolithic apps that can also live as Docker containers.
|
||||||
<p>
|
<p>
|
||||||
There are also additional eBooks focusing on Containers/Docker lifecycle (DevOps, CI/CD, etc.) with Microsoft Tools, already published plus an additional eBook focusing on Enterprise Apps Patterns with Xamarin.Forms.
|
There are also additional eBooks focusing on Containers/Docker lifecycle (DevOps, CI/CD, etc.) with Microsoft Tools, already published plus an additional eBook focusing on Enterprise Apps Patterns with Xamarin.Forms.
|
||||||
You can download them and start reviewing these Guides/eBooks here:
|
You can download them and start reviewing these Guides/eBooks here:
|
||||||
@ -41,9 +42,9 @@ You can download them and start reviewing these Guides/eBooks here:
|
|||||||
| Architecting & Developing | Containers Lifecycle & CI/CD | App patterns with Xamarin.Forms |
|
| 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> |
|
| <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** (Early DRAFT, still work in progress)</a> </sup> | <sup> <a href='https://aka.ms/dockerlifecycleebook'>**Download** (First Edition from late 2016) </a> </sup> | <sup> <a href='https://aka.ms/xamarinpatternsebook'>**Download** (Early DRAFT, still work in progress) </a> </sup> |
|
| <sup> <a href='https://aka.ms/microservicesebook'>**Download** (Early DRAFT, still work in progress)</a> </sup> | <sup> <a href='https://aka.ms/dockerlifecycleebook'>**Download** (First Edition from late 2016) </a> </sup> | <sup> <a href='https://aka.ms/xamarinpatternsebook'>**Download** (Preview Edition) </a> </sup> |
|
||||||
|
|
||||||
Send feedback to [cesardl@microsoft.com](cesardl@microsoft.com)
|
Send feedback to [dotnet-architecture-ebooks-feedback@service.microsoft.com](dotnet-architecture-ebooks-feedback@service.microsoft.com)
|
||||||
<p>
|
<p>
|
||||||
However, we encourage to download and review the "Architecting & Developing eBook" because the architectural styles and architectural patterns and technologies explained in the guidance are using this reference application when explaining many pattern implementations, so you'll understand much better the context, design and decisions taken in the current architecture and internal designs.
|
However, we encourage to download and review the "Architecting & Developing eBook" because the architectural styles and architectural patterns and technologies explained in the guidance are using this reference application when explaining many pattern implementations, so you'll understand much better the context, design and decisions taken in the current architecture and internal designs.
|
||||||
|
|
||||||
|
@ -45,7 +45,6 @@ services:
|
|||||||
- ASPNETCORE_URLS=http://0.0.0.0:5102
|
- ASPNETCORE_URLS=http://0.0.0.0:5102
|
||||||
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
|
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
|
||||||
- identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
|
- identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
|
||||||
- BasketUrl=http://basket.api:5103
|
|
||||||
- EventBusConnection=rabbitmq
|
- EventBusConnection=rabbitmq
|
||||||
ports:
|
ports:
|
||||||
- "5102:5102"
|
- "5102:5102"
|
||||||
|
@ -50,7 +50,6 @@ services:
|
|||||||
- ASPNETCORE_URLS=http://0.0.0.0:5102
|
- ASPNETCORE_URLS=http://0.0.0.0:5102
|
||||||
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
|
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
|
||||||
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
|
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
|
||||||
- BasketUrl=http://basket.api:5103
|
|
||||||
- EventBusConnection=rabbitmq
|
- EventBusConnection=rabbitmq
|
||||||
ports:
|
ports:
|
||||||
- "5102:5102"
|
- "5102:5102"
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26403.3
|
VisualStudioVersion = 15.0.26403.7
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
|
||||||
EndProject
|
EndProject
|
||||||
@ -62,12 +62,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationEventLogEF", "sr
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks", "HealthChecks", "{A81ECBC2-6B00-4DCD-8388-469174033379}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks", "HealthChecks", "{A81ECBC2-6B00-4DCD-8388-469174033379}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj", "{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}"
|
|
||||||
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}"
|
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
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.Data", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj", "{7804FC60-23E6-490C-8E08-F9FEF829F184}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience", "{FBF43D93-F2E7-4FF8-B4AB-186895949B88}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience", "{FBF43D93-F2E7-4FF8-B4AB-186895949B88}"
|
||||||
@ -78,6 +74,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}"
|
||||||
EndProject
|
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
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||||
@ -716,54 +716,6 @@ Global
|
|||||||
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x64.Build.0 = Release|Any CPU
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.Build.0 = Release|Any CPU
|
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x64.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x86.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.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.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|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.ActiveCfg = Debug|Any CPU
|
||||||
@ -812,54 +764,6 @@ Global
|
|||||||
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x64.Build.0 = 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.ActiveCfg = Release|Any CPU
|
||||||
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.Build.0 = Release|Any CPU
|
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x64.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x86.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|ARM.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|ARM.ActiveCfg = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|ARM.Build.0 = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184}.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.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|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||||
@ -1004,6 +908,150 @@ Global
|
|||||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.Build.0 = Release|Any CPU
|
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.ActiveCfg = Release|Any CPU
|
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.Build.0 = Release|Any CPU
|
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{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
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x64.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x86.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -1033,12 +1081,13 @@ Global
|
|||||||
{8088F3FC-6787-45FA-A924-816EC81CBFAC} = {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}
|
{9EE28E45-1533-472B-8267-56C48855BA0E} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
|
||||||
{A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
|
{A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
|
||||||
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
|
||||||
{942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
{942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
||||||
{7804FC60-23E6-490C-8E08-F9FEF829F184} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
|
||||||
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
|
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
|
||||||
{FBF43D93-F2E7-4FF8-B4AB-186895949B88} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
|
{FBF43D93-F2E7-4FF8-B4AB-186895949B88} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
|
||||||
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502} = {FBF43D93-F2E7-4FF8-B4AB-186895949B88}
|
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502} = {FBF43D93-F2E7-4FF8-B4AB-186895949B88}
|
||||||
|
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
||||||
|
{4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379}
|
||||||
|
{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
|
||||||
{022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
|
{022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
|
||||||
{1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5}
|
{1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 38 KiB |
BIN
img/ebook_arch_dev_microservices_containers_cover_LARGE.png
Normal file
BIN
img/ebook_arch_dev_microservices_containers_cover_LARGE.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 242 KiB |
BIN
img/ebook_arch_dev_microservices_containers_cover_OLD.png
Normal file
BIN
img/ebook_arch_dev_microservices_containers_cover_OLD.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 25 KiB |
@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.2.0" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
|
||||||
|
<ProjectReference Include="..\EventBus\EventBus.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,56 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace EventBus.Tests
|
||||||
|
{
|
||||||
|
public class InMemory_SubscriptionManager_Tests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void After_Creation_Should_Be_Empty()
|
||||||
|
{
|
||||||
|
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||||
|
Assert.True(manager.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void After_One_Event_Subscription_Should_Contain_The_Event()
|
||||||
|
{
|
||||||
|
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||||
|
manager.AddSubscription<TestIntegrationEvent,TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
|
||||||
|
Assert.True(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void After_All_Subscriptions_Are_Deleted_Event_Should_No_Longer_Exists()
|
||||||
|
{
|
||||||
|
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||||
|
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
|
||||||
|
manager.RemoveSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
|
||||||
|
Assert.False(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Deleting_Last_Subscription_Should_Raise_On_Deleted_Event()
|
||||||
|
{
|
||||||
|
bool raised = false;
|
||||||
|
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||||
|
manager.OnEventRemoved += (o, e) => raised = true;
|
||||||
|
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
|
||||||
|
manager.RemoveSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
|
||||||
|
Assert.True(raised);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Get_Handlers_For_Event_Should_Return_All_Handlers()
|
||||||
|
{
|
||||||
|
var manager = new InMemoryEventBusSubscriptionsManager();
|
||||||
|
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
|
||||||
|
manager.AddSubscription<TestIntegrationEvent, TestIntegrationOtherEventHandler>(() => new TestIntegrationOtherEventHandler());
|
||||||
|
var handlers = manager.GetHandlersForEvent<TestIntegrationEvent>();
|
||||||
|
Assert.Equal(2, handlers.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace EventBus.Tests
|
||||||
|
{
|
||||||
|
public class TestIntegrationEvent : IntegrationEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace EventBus.Tests
|
||||||
|
{
|
||||||
|
public class TestIntegrationOtherEventHandler : IIntegrationEventHandler<TestIntegrationEvent>
|
||||||
|
{
|
||||||
|
public bool Handled { get; private set; }
|
||||||
|
|
||||||
|
public TestIntegrationOtherEventHandler()
|
||||||
|
{
|
||||||
|
Handled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(TestIntegrationEvent @event)
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace EventBus.Tests
|
||||||
|
{
|
||||||
|
public class TestIntegrationEventHandler : IIntegrationEventHandler<TestIntegrationEvent>
|
||||||
|
{
|
||||||
|
public bool Handled { get; private set; }
|
||||||
|
|
||||||
|
public TestIntegrationEventHandler()
|
||||||
|
{
|
||||||
|
Handled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(TestIntegrationEvent @event)
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,17 @@
|
|||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
|
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
|
||||||
{
|
{
|
||||||
public interface IEventBus
|
public interface IEventBus
|
||||||
{
|
{
|
||||||
void Subscribe<T>(IIntegrationEventHandler<T> handler) where T: IntegrationEvent;
|
void Subscribe<T, TH>(Func<TH> handler)
|
||||||
void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent;
|
where T : IntegrationEvent
|
||||||
|
where TH : IIntegrationEventHandler<T>;
|
||||||
|
void Unsubscribe<T, TH>()
|
||||||
|
where TH : IIntegrationEventHandler<T>
|
||||||
|
where T : IntegrationEvent;
|
||||||
|
|
||||||
void Publish(IntegrationEvent @event);
|
void Publish(IntegrationEvent @event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
|
||||||
|
{
|
||||||
|
public interface IEventBusSubscriptionsManager
|
||||||
|
{
|
||||||
|
bool IsEmpty { get; }
|
||||||
|
event EventHandler<string> OnEventRemoved;
|
||||||
|
void AddSubscription<T, TH>(Func<TH> handler)
|
||||||
|
where T : IntegrationEvent
|
||||||
|
where TH : IIntegrationEventHandler<T>;
|
||||||
|
|
||||||
|
void RemoveSubscription<T, TH>()
|
||||||
|
where TH : IIntegrationEventHandler<T>
|
||||||
|
where T : IntegrationEvent;
|
||||||
|
bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent;
|
||||||
|
bool HasSubscriptionsForEvent(string eventName);
|
||||||
|
Type GetEventTypeByName(string eventName);
|
||||||
|
void Clear();
|
||||||
|
IEnumerable<Delegate> GetHandlersForEvent<T>() where T : IntegrationEvent;
|
||||||
|
IEnumerable<Delegate> GetHandlersForEvent(string eventName);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
|
||||||
|
{
|
||||||
|
public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, List<Delegate>> _handlers;
|
||||||
|
private readonly List<Type> _eventTypes;
|
||||||
|
|
||||||
|
public event EventHandler<string> OnEventRemoved;
|
||||||
|
|
||||||
|
public InMemoryEventBusSubscriptionsManager()
|
||||||
|
{
|
||||||
|
_handlers = new Dictionary<string, List<Delegate>>();
|
||||||
|
_eventTypes = new List<Type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEmpty => !_handlers.Keys.Any();
|
||||||
|
public void Clear() => _handlers.Clear();
|
||||||
|
|
||||||
|
public void AddSubscription<T, TH>(Func<TH> handler)
|
||||||
|
where T : IntegrationEvent
|
||||||
|
where TH : IIntegrationEventHandler<T>
|
||||||
|
{
|
||||||
|
var key = GetEventKey<T>();
|
||||||
|
if (!HasSubscriptionsForEvent<T>())
|
||||||
|
{
|
||||||
|
_handlers.Add(key, new List<Delegate>());
|
||||||
|
}
|
||||||
|
_handlers[key].Add(handler);
|
||||||
|
_eventTypes.Add(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveSubscription<T, TH>()
|
||||||
|
where TH : IIntegrationEventHandler<T>
|
||||||
|
where T : IntegrationEvent
|
||||||
|
{
|
||||||
|
var handlerToRemove = FindHandlerToRemove<T, TH>();
|
||||||
|
if (handlerToRemove != null)
|
||||||
|
{
|
||||||
|
var key = GetEventKey<T>();
|
||||||
|
_handlers[key].Remove(handlerToRemove);
|
||||||
|
if (!_handlers[key].Any())
|
||||||
|
{
|
||||||
|
_handlers.Remove(key);
|
||||||
|
var eventType = _eventTypes.SingleOrDefault(e => e.Name == key);
|
||||||
|
if (eventType != null)
|
||||||
|
{
|
||||||
|
_eventTypes.Remove(eventType);
|
||||||
|
RaiseOnEventRemoved(eventType.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Delegate> GetHandlersForEvent<T>() where T : IntegrationEvent
|
||||||
|
{
|
||||||
|
var key = GetEventKey<T>();
|
||||||
|
return GetHandlersForEvent(key);
|
||||||
|
}
|
||||||
|
public IEnumerable<Delegate> GetHandlersForEvent(string eventName) => _handlers[eventName];
|
||||||
|
|
||||||
|
private void RaiseOnEventRemoved(string eventName)
|
||||||
|
{
|
||||||
|
var handler = OnEventRemoved;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
OnEventRemoved(this, eventName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Delegate FindHandlerToRemove<T, TH>()
|
||||||
|
where T : IntegrationEvent
|
||||||
|
where TH : IIntegrationEventHandler<T>
|
||||||
|
{
|
||||||
|
if (!HasSubscriptionsForEvent<T>())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = GetEventKey<T>();
|
||||||
|
foreach (var func in _handlers[key])
|
||||||
|
{
|
||||||
|
var genericArgs = func.GetType().GetGenericArguments();
|
||||||
|
if (genericArgs.SingleOrDefault() == typeof(TH))
|
||||||
|
{
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent
|
||||||
|
{
|
||||||
|
var key = GetEventKey<T>();
|
||||||
|
return HasSubscriptionsForEvent(key);
|
||||||
|
}
|
||||||
|
public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName);
|
||||||
|
|
||||||
|
public Type GetEventTypeByName(string eventName) => _eventTypes.Single(t => t.Name == eventName);
|
||||||
|
|
||||||
|
private string GetEventKey<T>()
|
||||||
|
{
|
||||||
|
return typeof(T).Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,18 +10,18 @@ using System.Net.Sockets;
|
|||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||||
{
|
{
|
||||||
public class DefaultRabbitMQPersisterConnection
|
public class DefaultRabbitMQPersistentConnection
|
||||||
: IRabbitMQPersisterConnection
|
: IRabbitMQPersistentConnection
|
||||||
{
|
{
|
||||||
private readonly IConnectionFactory _connectionFactory;
|
private readonly IConnectionFactory _connectionFactory;
|
||||||
private readonly ILogger<DefaultRabbitMQPersisterConnection> _logger;
|
private readonly ILogger<DefaultRabbitMQPersistentConnection> _logger;
|
||||||
|
|
||||||
IConnection _connection;
|
IConnection _connection;
|
||||||
bool _disposed;
|
bool _disposed;
|
||||||
|
|
||||||
object sync_root = new object();
|
object sync_root = new object();
|
||||||
|
|
||||||
public DefaultRabbitMQPersisterConnection(IConnectionFactory connectionFactory,ILogger<DefaultRabbitMQPersisterConnection> logger)
|
public DefaultRabbitMQPersistentConnection(IConnectionFactory connectionFactory,ILogger<DefaultRabbitMQPersistentConnection> logger)
|
||||||
{
|
{
|
||||||
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
|
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
@ -87,13 +87,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
|||||||
_connection.CallbackException += OnCallbackException;
|
_connection.CallbackException += OnCallbackException;
|
||||||
_connection.ConnectionBlocked += OnConnectionBlocked;
|
_connection.ConnectionBlocked += OnConnectionBlocked;
|
||||||
|
|
||||||
_logger.LogInformation($"RabbitMQ persister connection acquire a connection {_connection.Endpoint.HostName} and is subscribed to failure events");
|
_logger.LogInformation($"RabbitMQ persistent connection acquired a connection {_connection.Endpoint.HostName} and is subscribed to failure events");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogCritical("FATAL ERROR: RabbitMQ connections can't be created and opened");
|
_logger.LogCritical("FATAL ERROR: RabbitMQ connections could not be created and opened");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -21,32 +22,50 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
|||||||
{
|
{
|
||||||
const string BROKER_NAME = "eshop_event_bus";
|
const string BROKER_NAME = "eshop_event_bus";
|
||||||
|
|
||||||
private readonly IRabbitMQPersisterConnection _persisterConnection;
|
private readonly IRabbitMQPersistentConnection _persistentConnection;
|
||||||
private readonly ILogger<EventBusRabbitMQ> _logger;
|
private readonly ILogger<EventBusRabbitMQ> _logger;
|
||||||
|
private readonly IEventBusSubscriptionsManager _subsManager;
|
||||||
private readonly Dictionary<string, List<IIntegrationEventHandler>> _handlers
|
|
||||||
= new Dictionary<string, List<IIntegrationEventHandler>>();
|
|
||||||
|
|
||||||
private readonly List<Type> _eventTypes
|
|
||||||
= new List<Type>();
|
|
||||||
|
|
||||||
private IModel _consumerChannel;
|
private IModel _consumerChannel;
|
||||||
private string _queueName;
|
private string _queueName;
|
||||||
|
|
||||||
public EventBusRabbitMQ(IRabbitMQPersisterConnection persisterConnection, ILogger<EventBusRabbitMQ> logger)
|
public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger, IEventBusSubscriptionsManager subsManager)
|
||||||
{
|
{
|
||||||
_persisterConnection = persisterConnection ?? throw new ArgumentNullException(nameof(persisterConnection));
|
_persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection));
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager();
|
||||||
_consumerChannel = CreateConsumerChannel();
|
_consumerChannel = CreateConsumerChannel();
|
||||||
|
|
||||||
|
_subsManager.OnEventRemoved += SubsManager_OnEventRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SubsManager_OnEventRemoved(object sender, string eventName)
|
||||||
|
{
|
||||||
|
if (!_persistentConnection.IsConnected)
|
||||||
|
{
|
||||||
|
_persistentConnection.TryConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var channel = _persistentConnection.CreateModel())
|
||||||
|
{
|
||||||
|
channel.QueueUnbind(queue: _queueName,
|
||||||
|
exchange: BROKER_NAME,
|
||||||
|
routingKey: eventName);
|
||||||
|
|
||||||
|
if (_subsManager.IsEmpty)
|
||||||
|
{
|
||||||
|
_queueName = string.Empty;
|
||||||
|
_consumerChannel.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Publish(IntegrationEvent @event)
|
public void Publish(IntegrationEvent @event)
|
||||||
{
|
{
|
||||||
if (!_persisterConnection.IsConnected)
|
if (!_persistentConnection.IsConnected)
|
||||||
{
|
{
|
||||||
_persisterConnection.TryConnect();
|
_persistentConnection.TryConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
var policy = RetryPolicy.Handle<BrokerUnreachableException>()
|
var policy = RetryPolicy.Handle<BrokerUnreachableException>()
|
||||||
@ -56,7 +75,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
|||||||
_logger.LogWarning(ex.ToString());
|
_logger.LogWarning(ex.ToString());
|
||||||
});
|
});
|
||||||
|
|
||||||
using (var channel = _persisterConnection.CreateModel())
|
using (var channel = _persistentConnection.CreateModel())
|
||||||
{
|
{
|
||||||
var eventName = @event.GetType()
|
var eventName = @event.GetType()
|
||||||
.Name;
|
.Name;
|
||||||
@ -77,75 +96,49 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Subscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent
|
public void Subscribe<T, TH>(Func<TH> handler)
|
||||||
|
where T : IntegrationEvent
|
||||||
|
where TH : IIntegrationEventHandler<T>
|
||||||
{
|
{
|
||||||
var eventName = typeof(T).Name;
|
var eventName = typeof(T).Name;
|
||||||
|
var containsKey = _subsManager.HasSubscriptionsForEvent<T>();
|
||||||
if (_handlers.ContainsKey(eventName))
|
if (!containsKey)
|
||||||
{
|
{
|
||||||
_handlers[eventName].Add(handler);
|
if (!_persistentConnection.IsConnected)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_persisterConnection.IsConnected)
|
|
||||||
{
|
{
|
||||||
_persisterConnection.TryConnect();
|
_persistentConnection.TryConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var channel = _persisterConnection.CreateModel())
|
using (var channel = _persistentConnection.CreateModel())
|
||||||
{
|
{
|
||||||
channel.QueueBind(queue: _queueName,
|
channel.QueueBind(queue: _queueName,
|
||||||
exchange: BROKER_NAME,
|
exchange: BROKER_NAME,
|
||||||
routingKey: eventName);
|
routingKey: eventName);
|
||||||
|
|
||||||
_handlers.Add(eventName, new List<IIntegrationEventHandler>());
|
|
||||||
_handlers[eventName].Add(handler);
|
|
||||||
_eventTypes.Add(typeof(T));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_subsManager.AddSubscription<T, TH>(handler);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent
|
public void Unsubscribe<T, TH>()
|
||||||
|
where TH : IIntegrationEventHandler<T>
|
||||||
|
where T : IntegrationEvent
|
||||||
{
|
{
|
||||||
var eventName = typeof(T).Name;
|
_subsManager.RemoveSubscription<T, TH>();
|
||||||
|
}
|
||||||
|
|
||||||
if (_handlers.ContainsKey(eventName) && _handlers[eventName].Contains(handler))
|
private static Func<IIntegrationEventHandler> FindHandlerByType(Type handlerType, IEnumerable<Func<IIntegrationEventHandler>> handlers)
|
||||||
|
{
|
||||||
|
foreach (var func in handlers)
|
||||||
{
|
{
|
||||||
_handlers[eventName].Remove(handler);
|
if (func.GetMethodInfo().ReturnType == handlerType)
|
||||||
|
|
||||||
if (_handlers[eventName].Count == 0)
|
|
||||||
{
|
{
|
||||||
_handlers.Remove(eventName);
|
return func;
|
||||||
|
|
||||||
var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName);
|
|
||||||
|
|
||||||
if (eventType != null)
|
|
||||||
{
|
|
||||||
_eventTypes.Remove(eventType);
|
|
||||||
|
|
||||||
if (!_persisterConnection.IsConnected)
|
|
||||||
{
|
|
||||||
_persisterConnection.TryConnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var channel = _persisterConnection.CreateModel())
|
|
||||||
{
|
|
||||||
channel.QueueUnbind(queue: _queueName,
|
|
||||||
exchange: BROKER_NAME,
|
|
||||||
routingKey: eventName);
|
|
||||||
|
|
||||||
if (_handlers.Keys.Count == 0)
|
|
||||||
{
|
|
||||||
_queueName = string.Empty;
|
|
||||||
|
|
||||||
_consumerChannel.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -154,18 +147,18 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
|||||||
{
|
{
|
||||||
_consumerChannel.Dispose();
|
_consumerChannel.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlers.Clear();
|
_subsManager.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IModel CreateConsumerChannel()
|
private IModel CreateConsumerChannel()
|
||||||
{
|
{
|
||||||
if (!_persisterConnection.IsConnected)
|
if (!_persistentConnection.IsConnected)
|
||||||
{
|
{
|
||||||
_persisterConnection.TryConnect();
|
_persistentConnection.TryConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
var channel = _persisterConnection.CreateModel();
|
var channel = _persistentConnection.CreateModel();
|
||||||
|
|
||||||
channel.ExchangeDeclare(exchange: BROKER_NAME,
|
channel.ExchangeDeclare(exchange: BROKER_NAME,
|
||||||
type: "direct");
|
type: "direct");
|
||||||
@ -196,15 +189,17 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
|||||||
|
|
||||||
private async Task ProcessEvent(string eventName, string message)
|
private async Task ProcessEvent(string eventName, string message)
|
||||||
{
|
{
|
||||||
if (_handlers.ContainsKey(eventName))
|
|
||||||
{
|
|
||||||
Type eventType = _eventTypes.Single(t => t.Name == eventName);
|
|
||||||
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
|
|
||||||
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
|
|
||||||
var handlers = _handlers[eventName];
|
|
||||||
|
|
||||||
foreach (var handler in handlers)
|
if (_subsManager.HasSubscriptionsForEvent(eventName))
|
||||||
|
{
|
||||||
|
var eventType = _subsManager.GetEventTypeByName(eventName);
|
||||||
|
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
|
||||||
|
var handlers = _subsManager.GetHandlersForEvent(eventName);
|
||||||
|
|
||||||
|
foreach (var handlerfactory in handlers)
|
||||||
{
|
{
|
||||||
|
var handler = handlerfactory.DynamicInvoke();
|
||||||
|
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
|
||||||
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
|
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,7 @@ using System;
|
|||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||||
{
|
{
|
||||||
|
public interface IRabbitMQPersistentConnection
|
||||||
public interface IRabbitMQPersisterConnection
|
|
||||||
: IDisposable
|
: IDisposable
|
||||||
{
|
{
|
||||||
bool IsConnected { get; }
|
bool IsConnected { get; }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
@ -11,30 +13,34 @@ namespace Microsoft.AspNetCore.HealthChecks
|
|||||||
{
|
{
|
||||||
public class HealthCheckMiddleware
|
public class HealthCheckMiddleware
|
||||||
{
|
{
|
||||||
private RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private string _path;
|
private readonly string _path;
|
||||||
private int? _port;
|
private readonly int? _port;
|
||||||
private IHealthCheckService _service;
|
private readonly IHealthCheckService _service;
|
||||||
|
private readonly TimeSpan _timeout;
|
||||||
|
|
||||||
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port)
|
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
_port = port;
|
_port = port;
|
||||||
_service = service;
|
_service = service;
|
||||||
_next = next;
|
_next = next;
|
||||||
|
_timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path)
|
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
_path = path;
|
_path = path;
|
||||||
_service = service;
|
_service = service;
|
||||||
_next = next;
|
_next = next;
|
||||||
|
_timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(HttpContext context)
|
||||||
{
|
{
|
||||||
if (IsHealthCheckRequest(context))
|
if (IsHealthCheckRequest(context))
|
||||||
{
|
{
|
||||||
var result = await _service.CheckHealthAsync();
|
var timeoutTokenSource = new CancellationTokenSource(_timeout);
|
||||||
|
var result = await _service.CheckHealthAsync(timeoutTokenSource.Token);
|
||||||
var status = result.CheckStatus;
|
var status = result.CheckStatus;
|
||||||
|
|
||||||
if (status != CheckStatus.Healthy)
|
if (status != CheckStatus.Healthy)
|
||||||
@ -60,7 +66,9 @@ namespace Microsoft.AspNetCore.HealthChecks
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (context.Request.Path == _path)
|
if (context.Request.Path == _path)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -11,15 +11,18 @@ namespace Microsoft.AspNetCore.HealthChecks
|
|||||||
{
|
{
|
||||||
private string _path;
|
private string _path;
|
||||||
private int? _port;
|
private int? _port;
|
||||||
|
private TimeSpan _timeout;
|
||||||
|
|
||||||
public HealthCheckStartupFilter(int port)
|
public HealthCheckStartupFilter(int port, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
_port = port;
|
_port = port;
|
||||||
|
_timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HealthCheckStartupFilter(string path)
|
public HealthCheckStartupFilter(string path, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
_path = path;
|
_path = path;
|
||||||
|
_timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
||||||
@ -27,9 +30,13 @@ namespace Microsoft.AspNetCore.HealthChecks
|
|||||||
return app =>
|
return app =>
|
||||||
{
|
{
|
||||||
if (_port.HasValue)
|
if (_port.HasValue)
|
||||||
app.UseMiddleware<HealthCheckMiddleware>(_port);
|
{
|
||||||
|
app.UseMiddleware<HealthCheckMiddleware>(_port, _timeout);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
app.UseMiddleware<HealthCheckMiddleware>(_path);
|
{
|
||||||
|
app.UseMiddleware<HealthCheckMiddleware>(_path, _timeout);
|
||||||
|
}
|
||||||
|
|
||||||
next(app);
|
next(app);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.AspNetCore.HealthChecks;
|
using Microsoft.AspNetCore.HealthChecks;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@ -8,28 +9,38 @@ namespace Microsoft.AspNetCore.Hosting
|
|||||||
{
|
{
|
||||||
public static class HealthCheckWebHostBuilderExtension
|
public static class HealthCheckWebHostBuilderExtension
|
||||||
{
|
{
|
||||||
|
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port)
|
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(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 =>
|
builder.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
var existingUrl = builder.GetSetting(WebHostDefaults.ServerUrlsKey);
|
var existingUrl = builder.GetSetting(WebHostDefaults.ServerUrlsKey);
|
||||||
builder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};http://localhost:{port}");
|
builder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};http://localhost:{port}");
|
||||||
|
|
||||||
services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(port));
|
services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(port, timeout));
|
||||||
});
|
});
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path)
|
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);
|
Guard.ArgumentNotNull(nameof(path), path);
|
||||||
// REVIEW: Is there a better URL path validator somewhere?
|
// REVIEW: Is there a better URL path validator somewhere?
|
||||||
Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values");
|
Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values.");
|
||||||
Guard.ArgumentValid(path.StartsWith("/"), nameof(path), "Path should start with /");
|
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)));
|
builder.ConfigureServices(services => services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(path, timeout)));
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ using System.Data.SqlClient;
|
|||||||
|
|
||||||
namespace Microsoft.Extensions.HealthChecks
|
namespace Microsoft.Extensions.HealthChecks
|
||||||
{
|
{
|
||||||
public static class HealthCheckBuilderDataExtensions
|
public static class HealthCheckBuilderSqlServerExtensions
|
||||||
{
|
{
|
||||||
public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString)
|
public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString)
|
||||||
{
|
{
|
||||||
@ -33,7 +33,7 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}");
|
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}");
|
||||||
}
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,96 +15,102 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromCheck(check, builder.DefaultCacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check)
|
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromCheck(check, builder.DefaultCacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check, TimeSpan cacheDuration)
|
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check, TimeSpan cacheDuration)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromCheck(check, cacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration)
|
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromCheck(check, cacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check)
|
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromTaskCheck(check, builder.DefaultCacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check)
|
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromTaskCheck(check, builder.DefaultCacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromTaskCheck(check, cacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromTaskCheck(check, cacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check)
|
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, builder.DefaultCacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
|
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, builder.DefaultCacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, cacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration);
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
|
|
||||||
builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, cacheDuration));
|
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration);
|
||||||
return builder;
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
where T : IComparable<T>
|
where T : IComparable<T>
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
|
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
|
||||||
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
|
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
|
||||||
|
|
||||||
builder.AddCheck(name, () =>
|
builder.AddCheck(name, () =>
|
||||||
@ -23,7 +23,7 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
var status = currentValue.CompareTo(minValue) >= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
|
var status = currentValue.CompareTo(minValue) >= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
|
||||||
return HealthCheckResult.FromStatus(
|
return HealthCheckResult.FromStatus(
|
||||||
status,
|
status,
|
||||||
$"{name}: min={minValue}, current={currentValue}",
|
$"min={minValue}, current={currentValue}",
|
||||||
new Dictionary<string, object> { { "min", minValue }, { "current", currentValue } }
|
new Dictionary<string, object> { { "min", minValue }, { "current", currentValue } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -35,16 +35,16 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
where T : IComparable<T>
|
where T : IComparable<T>
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
|
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
|
||||||
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
|
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
|
||||||
|
|
||||||
builder.AddCheck($"{name}", () =>
|
builder.AddCheck(name, () =>
|
||||||
{
|
{
|
||||||
var currentValue = currentValueFunc();
|
var currentValue = currentValueFunc();
|
||||||
var status = currentValue.CompareTo(maxValue) <= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
|
var status = currentValue.CompareTo(maxValue) <= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
|
||||||
return HealthCheckResult.FromStatus(
|
return HealthCheckResult.FromStatus(
|
||||||
status,
|
status,
|
||||||
$"{name}: max={maxValue}, current={currentValue}",
|
$"max={maxValue}, current={currentValue}",
|
||||||
new Dictionary<string, object> { { "max", maxValue }, { "current", currentValue } }
|
new Dictionary<string, object> { { "max", maxValue }, { "current", currentValue } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.HealthChecks.Internal;
|
using Microsoft.Extensions.HealthChecks.Internal;
|
||||||
@ -37,73 +35,12 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
|
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
Guard.ArgumentNotNull(nameof(builder), builder);
|
||||||
Guard.ArgumentNotNullOrWhitespace(nameof(url), url);
|
Guard.ArgumentNotNullOrEmpty(nameof(url), url);
|
||||||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
||||||
|
|
||||||
var urlCheck = new UrlChecker(checkFunc, url);
|
var urlCheck = new UrlChecker(checkFunc, url);
|
||||||
builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync());
|
builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync());
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName)
|
|
||||||
=> AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => UrlChecker.DefaultUrlCheck(response));
|
|
||||||
|
|
||||||
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
|
|
||||||
Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
|
||||||
|
|
||||||
return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
|
|
||||||
Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
|
||||||
|
|
||||||
return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
|
|
||||||
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
|
||||||
|
|
||||||
return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => checkFunc(response));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
|
|
||||||
CheckStatus partialSuccessStatus)
|
|
||||||
=> AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => UrlChecker.DefaultUrlCheck(response));
|
|
||||||
|
|
||||||
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
|
|
||||||
CheckStatus partialSuccessStatus, Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
|
||||||
|
|
||||||
return AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
|
|
||||||
CheckStatus partialSuccessStatus, Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
|
||||||
|
|
||||||
return AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
|
|
||||||
CheckStatus partialSuccessStatus, Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
|
|
||||||
{
|
|
||||||
var urls = urlItems?.ToArray();
|
|
||||||
|
|
||||||
Guard.ArgumentNotNull(nameof(builder), builder);
|
|
||||||
Guard.ArgumentNotNullOrEmpty(nameof(urlItems), urls);
|
|
||||||
Guard.ArgumentNotNullOrWhitespace(nameof(groupName), groupName);
|
|
||||||
|
|
||||||
var urlChecker = new UrlChecker(checkFunc, urls) { PartiallyHealthyStatus = partialSuccessStatus };
|
|
||||||
builder.AddCheck($"UrlChecks({groupName})", () => urlChecker.CheckAsync());
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Microsoft.Extensions.HealthChecks
|
namespace Microsoft.Extensions.HealthChecks
|
||||||
{
|
{
|
||||||
// REVIEW: Does this need to be thread safe?
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a composite health check result built from several results.
|
/// Represents a composite health check result built from several results.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -31,17 +30,23 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
{
|
{
|
||||||
var checkStatuses = new HashSet<CheckStatus>(_results.Select(x => x.Value.CheckStatus));
|
var checkStatuses = new HashSet<CheckStatus>(_results.Select(x => x.Value.CheckStatus));
|
||||||
if (checkStatuses.Count == 0)
|
if (checkStatuses.Count == 0)
|
||||||
|
{
|
||||||
return _initialStatus;
|
return _initialStatus;
|
||||||
|
}
|
||||||
if (checkStatuses.Count == 1)
|
if (checkStatuses.Count == 1)
|
||||||
|
{
|
||||||
return checkStatuses.First();
|
return checkStatuses.First();
|
||||||
|
}
|
||||||
if (checkStatuses.Contains(CheckStatus.Healthy))
|
if (checkStatuses.Contains(CheckStatus.Healthy))
|
||||||
|
{
|
||||||
return _partiallyHealthyStatus;
|
return _partiallyHealthyStatus;
|
||||||
|
}
|
||||||
|
|
||||||
return CheckStatus.Unhealthy;
|
return CheckStatus.Unhealthy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Description => string.Join(Environment.NewLine, _results.Select(r => r.Value.Description));
|
public string Description => string.Join(Environment.NewLine, _results.Select(r => $"{r.Key}: {r.Value.Description}"));
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, object> Data
|
public IReadOnlyDictionary<string, object> Data
|
||||||
{
|
{
|
||||||
@ -58,23 +63,21 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
|
|
||||||
public IReadOnlyDictionary<string, IHealthCheckResult> Results => _results;
|
public IReadOnlyDictionary<string, IHealthCheckResult> Results => _results;
|
||||||
|
|
||||||
// REVIEW: Should description be required? Seems redundant for success checks.
|
|
||||||
|
|
||||||
public void Add(string name, CheckStatus status, string description)
|
public void Add(string name, CheckStatus status, string description)
|
||||||
=> Add(name, status, description, null);
|
=> Add(name, status, description, null);
|
||||||
|
|
||||||
public void Add(string name, CheckStatus status, string description, Dictionary<string, object> data)
|
public void Add(string name, CheckStatus status, string description, Dictionary<string, object> data)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
|
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
|
||||||
Guard.ArgumentValid(status != CheckStatus.Unknown, nameof(status), "Cannot add unknown status to composite health check result");
|
Guard.ArgumentValid(status != CheckStatus.Unknown, nameof(status), "Cannot add 'Unknown' status to composite health check result.");
|
||||||
Guard.ArgumentNotNullOrWhitespace(nameof(description), description);
|
Guard.ArgumentNotNullOrEmpty(nameof(description), description);
|
||||||
|
|
||||||
_results.Add(name, HealthCheckResult.FromStatus(status, description, data));
|
_results.Add(name, HealthCheckResult.FromStatus(status, description, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(string name, IHealthCheckResult checkResult)
|
public void Add(string name, IHealthCheckResult checkResult)
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
|
Guard.ArgumentNotNullOrEmpty(nameof(name), name);
|
||||||
Guard.ArgumentNotNull(nameof(checkResult), checkResult);
|
Guard.ArgumentNotNull(nameof(checkResult), checkResult);
|
||||||
|
|
||||||
_results.Add(name, checkResult);
|
_results.Add(name, checkResult);
|
||||||
|
@ -9,68 +9,34 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
{
|
{
|
||||||
public class HealthCheck : IHealthCheck
|
public class HealthCheck : IHealthCheck
|
||||||
{
|
{
|
||||||
private DateTimeOffset _cacheExpiration;
|
protected HealthCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
|
||||||
private IHealthCheckResult _cachedResult;
|
|
||||||
private volatile int _writerCount;
|
|
||||||
|
|
||||||
protected HealthCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(check), check);
|
Guard.ArgumentNotNull(nameof(check), check);
|
||||||
Guard.ArgumentValid(cacheDuration >= TimeSpan.Zero, nameof(cacheDuration), "Cache duration must either be zero (disabled) or a positive value");
|
|
||||||
|
|
||||||
Check = check;
|
Check = check;
|
||||||
CacheDuration = cacheDuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimeSpan CacheDuration { get; }
|
|
||||||
|
|
||||||
protected Func<CancellationToken, ValueTask<IHealthCheckResult>> Check { get; }
|
protected Func<CancellationToken, ValueTask<IHealthCheckResult>> Check { get; }
|
||||||
|
|
||||||
protected virtual DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
|
public ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
=> Check(cancellationToken);
|
||||||
|
|
||||||
public async ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken)
|
public static HealthCheck FromCheck(Func<IHealthCheckResult> check)
|
||||||
{
|
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()));
|
||||||
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
|
public static HealthCheck FromCheck(Func<CancellationToken, IHealthCheckResult> check)
|
||||||
{
|
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)));
|
||||||
_cachedResult = await Check(cancellationToken).ConfigureAwait(false);
|
|
||||||
_cacheExpiration = UtcNow + CacheDuration;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_writerCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _cachedResult;
|
public static HealthCheck FromTaskCheck(Func<Task<IHealthCheckResult>> check)
|
||||||
}
|
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()));
|
||||||
|
|
||||||
public static HealthCheck FromCheck(Func<IHealthCheckResult> check, TimeSpan cacheDuration)
|
public static HealthCheck FromTaskCheck(Func<CancellationToken, Task<IHealthCheckResult>> check)
|
||||||
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()), cacheDuration);
|
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)));
|
||||||
|
|
||||||
public static HealthCheck FromCheck(Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration)
|
public static HealthCheck FromValueTaskCheck(Func<ValueTask<IHealthCheckResult>> check)
|
||||||
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)), cacheDuration);
|
=> new HealthCheck(token => check());
|
||||||
|
|
||||||
public static HealthCheck FromTaskCheck(Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
public static HealthCheck FromValueTaskCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
|
||||||
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()), cacheDuration);
|
=> new HealthCheck(check);
|
||||||
|
|
||||||
public static HealthCheck FromTaskCheck(Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
|
||||||
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)), cacheDuration);
|
|
||||||
|
|
||||||
public static HealthCheck FromValueTaskCheck(Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
|
||||||
=> new HealthCheck(token => check(), cacheDuration);
|
|
||||||
|
|
||||||
public static HealthCheck FromValueTaskCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
|
|
||||||
=> new HealthCheck(check, cacheDuration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,33 +8,128 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
{
|
{
|
||||||
public class HealthCheckBuilder
|
public class HealthCheckBuilder
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, IHealthCheck> _checks;
|
private readonly Dictionary<string, CachedHealthCheck> _checksByName;
|
||||||
|
private readonly HealthCheckGroup _currentGroup;
|
||||||
|
private readonly Dictionary<string, HealthCheckGroup> _groups;
|
||||||
|
|
||||||
public HealthCheckBuilder()
|
public HealthCheckBuilder()
|
||||||
{
|
{
|
||||||
_checks = new Dictionary<string, IHealthCheck>(StringComparer.OrdinalIgnoreCase);
|
_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);
|
DefaultCacheDuration = TimeSpan.FromMinutes(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, IHealthCheck> Checks => _checks;
|
/// <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; }
|
public TimeSpan DefaultCacheDuration { get; private set; }
|
||||||
|
|
||||||
public HealthCheckBuilder AddCheck(string name, IHealthCheck check)
|
/// <summary>
|
||||||
{
|
/// Gets the registered groups, indexed by group name. The root group's name is <see cref="string.Empty"/>.
|
||||||
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
|
/// </summary>
|
||||||
Guard.ArgumentNotNull(nameof(check), check);
|
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);
|
||||||
|
|
||||||
_checks.Add(name, check);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HealthCheckBuilder WithDefaultCacheDuration(TimeSpan duration)
|
public HealthCheckBuilder WithDefaultCacheDuration(TimeSpan duration)
|
||||||
{
|
{
|
||||||
Guard.ArgumentValid(duration >= TimeSpan.Zero, nameof(duration), "Duration must be zero (disabled) or a positive duration");
|
Guard.ArgumentValid(duration >= TimeSpan.Zero, nameof(duration), "Duration must be zero (disabled) or a positive duration.");
|
||||||
|
|
||||||
DefaultCacheDuration = duration;
|
DefaultCacheDuration = duration;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HealthCheckBuilder WithPartialSuccessStatus(CheckStatus partiallyHealthyStatus)
|
||||||
|
{
|
||||||
|
_currentGroup.PartiallyHealthyStatus = partiallyHealthyStatus;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +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 static class HealthCheckExtensions
|
|
||||||
{
|
|
||||||
public static ValueTask<IHealthCheckResult> CheckAsync(this IHealthCheck healthCheck)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(healthCheck), healthCheck);
|
|
||||||
|
|
||||||
return healthCheck.CheckAsync(CancellationToken.None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,37 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@
|
|||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.Extensions.HealthChecks
|
namespace Microsoft.Extensions.HealthChecks
|
||||||
{
|
{
|
||||||
|
@ -3,52 +3,117 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.HealthChecks
|
namespace Microsoft.Extensions.HealthChecks
|
||||||
{
|
{
|
||||||
public class HealthCheckService : IHealthCheckService
|
public class HealthCheckService : IHealthCheckService
|
||||||
{
|
{
|
||||||
public IReadOnlyDictionary<string, IHealthCheck> _checks;
|
private readonly HealthCheckBuilder _builder;
|
||||||
|
private readonly IReadOnlyList<HealthCheckGroup> _groups;
|
||||||
|
private readonly HealthCheckGroup _root;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
|
|
||||||
private ILogger<HealthCheckService> _logger;
|
public HealthCheckService(HealthCheckBuilder builder, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory)
|
||||||
|
|
||||||
public HealthCheckService(HealthCheckBuilder builder, ILogger<HealthCheckService> logger)
|
|
||||||
{
|
{
|
||||||
_checks = builder.Checks;
|
_builder = builder;
|
||||||
_logger = logger;
|
_groups = GetGroups().Where(group => group.GroupName != string.Empty).ToList();
|
||||||
|
_root = GetGroup(string.Empty);
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CompositeHealthCheckResult> CheckHealthAsync(CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken)
|
public async Task<CompositeHealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var logMessage = new StringBuilder();
|
using (var scope = GetServiceScope())
|
||||||
var result = new CompositeHealthCheckResult(partiallyHealthyStatus);
|
|
||||||
|
|
||||||
foreach (var check in _checks)
|
|
||||||
{
|
{
|
||||||
try
|
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)
|
||||||
{
|
{
|
||||||
var healthCheckResult = await check.Value.CheckAsync().ConfigureAwait(false);
|
result.Add($"Group({groupTask.Group.GroupName})", groupTask.Task.Result);
|
||||||
logMessage.AppendLine($"HealthCheck: {check.Key} : {healthCheckResult.CheckStatus}");
|
|
||||||
result.Add(check.Key, healthCheckResult);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logMessage.AppendLine($"HealthCheck: {check.Key} : Exception {ex.GetType().FullName} thrown");
|
|
||||||
result.Add(check.Key, CheckStatus.Unhealthy, $"Exception during check: {ex.GetType().FullName}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logMessage.Length == 0)
|
|
||||||
logMessage.AppendLine("HealthCheck: No checks have been registered");
|
|
||||||
|
|
||||||
_logger.Log((result.CheckStatus == CheckStatus.Healthy ? LogLevel.Information : LogLevel.Error), 0, logMessage.ToString(), null, MessageFormatter);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string MessageFormatter(string state, Exception error) => state;
|
private class UnscopedServiceProvider : IServiceScope
|
||||||
|
{
|
||||||
|
public UnscopedServiceProvider(IServiceProvider serviceProvider)
|
||||||
|
=> ServiceProvider = serviceProvider;
|
||||||
|
|
||||||
|
public IServiceProvider ServiceProvider { get; }
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,28 @@
|
|||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.Extensions.HealthChecks;
|
using Microsoft.Extensions.HealthChecks;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
public static class HealthCheckServiceCollectionExtensions
|
public static class HealthCheckServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddHealthChecks(this IServiceCollection services, Action<HealthCheckBuilder> checkupAction)
|
private static readonly Type HealthCheckServiceInterface = typeof(IHealthCheckService);
|
||||||
|
|
||||||
|
public static IServiceCollection AddHealthChecks(this IServiceCollection services, Action<HealthCheckBuilder> checks)
|
||||||
{
|
{
|
||||||
var checkupBuilder = new HealthCheckBuilder();
|
Guard.OperationValid(!services.Any(descriptor => descriptor.ServiceType == HealthCheckServiceInterface), "AddHealthChecks may only be called once.");
|
||||||
|
|
||||||
checkupAction.Invoke(checkupBuilder);
|
var builder = new HealthCheckBuilder();
|
||||||
|
|
||||||
services.AddSingleton(checkupBuilder);
|
services.AddSingleton<IHealthCheckService, HealthCheckService>(serviceProvider =>
|
||||||
services.AddSingleton<IHealthCheckService, HealthCheckService>();
|
{
|
||||||
|
var serviceScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
|
||||||
|
return new HealthCheckService(builder, serviceProvider, serviceScopeFactory);
|
||||||
|
});
|
||||||
|
|
||||||
|
checks(builder);
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.Extensions.HealthChecks
|
|
||||||
{
|
|
||||||
public static class HealthCheckServiceExtensions
|
|
||||||
{
|
|
||||||
public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(service), service);
|
|
||||||
|
|
||||||
return service.CheckHealthAsync(CheckStatus.Unhealthy, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CheckStatus partiallyHealthyStatus)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(service), service);
|
|
||||||
|
|
||||||
return service.CheckHealthAsync(partiallyHealthyStatus, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(service), service);
|
|
||||||
|
|
||||||
return service.CheckHealthAsync(CheckStatus.Unhealthy, cancellationToken);
|
|
||||||
}
|
|
||||||
public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Guard.ArgumentNotNull(nameof(service), service);
|
|
||||||
|
|
||||||
return service.CheckHealthAsync(partiallyHealthyStatus, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -9,8 +8,6 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
{
|
{
|
||||||
public interface IHealthCheck
|
public interface IHealthCheck
|
||||||
{
|
{
|
||||||
TimeSpan CacheDuration { get; }
|
ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
|
||||||
ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -8,6 +10,49 @@ namespace Microsoft.Extensions.HealthChecks
|
|||||||
{
|
{
|
||||||
public interface IHealthCheckService
|
public interface IHealthCheckService
|
||||||
{
|
{
|
||||||
Task<CompositeHealthCheckResult> CheckHealthAsync(CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken);
|
/// <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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -13,55 +12,33 @@ namespace Microsoft.Extensions.HealthChecks.Internal
|
|||||||
public class UrlChecker
|
public class UrlChecker
|
||||||
{
|
{
|
||||||
private readonly Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> _checkFunc;
|
private readonly Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> _checkFunc;
|
||||||
private readonly string[] _urls;
|
private readonly string _url;
|
||||||
|
|
||||||
// REVIEW: Cache timeout here?
|
public UrlChecker(Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc, string url)
|
||||||
public UrlChecker(Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc, params string[] urls)
|
|
||||||
{
|
{
|
||||||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
|
||||||
Guard.ArgumentNotNullOrEmpty(nameof(urls), urls);
|
Guard.ArgumentNotNullOrEmpty(nameof(url), url);
|
||||||
|
|
||||||
_checkFunc = checkFunc;
|
_checkFunc = checkFunc;
|
||||||
_urls = urls;
|
_url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CheckStatus PartiallyHealthyStatus { get; set; } = CheckStatus.Warning;
|
public CheckStatus PartiallyHealthyStatus { get; set; } = CheckStatus.Warning;
|
||||||
|
|
||||||
public Task<IHealthCheckResult> CheckAsync()
|
public async Task<IHealthCheckResult> CheckAsync()
|
||||||
=> _urls.Length == 1 ? CheckSingleAsync() : CheckMultiAsync();
|
|
||||||
|
|
||||||
public async Task<IHealthCheckResult> CheckSingleAsync()
|
|
||||||
{
|
{
|
||||||
var httpClient = CreateHttpClient();
|
using (var httpClient = CreateHttpClient())
|
||||||
var result = default(IHealthCheckResult);
|
|
||||||
await CheckUrlAsync(httpClient, _urls[0], (_, checkResult) => result = checkResult).ConfigureAwait(false);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IHealthCheckResult> CheckMultiAsync()
|
|
||||||
{
|
|
||||||
var composite = new CompositeHealthCheckResult(PartiallyHealthyStatus);
|
|
||||||
var httpClient = CreateHttpClient();
|
|
||||||
|
|
||||||
// REVIEW: Should these be done in parallel?
|
|
||||||
foreach (var url in _urls)
|
|
||||||
await CheckUrlAsync(httpClient, url, (name, checkResult) => composite.Add(name, checkResult)).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return composite;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task CheckUrlAsync(HttpClient httpClient, string url, Action<string, IHealthCheckResult> adder)
|
|
||||||
{
|
|
||||||
var name = $"UrlCheck({url})";
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var response = await httpClient.GetAsync(url).ConfigureAwait(false);
|
try
|
||||||
var result = await _checkFunc(response);
|
{
|
||||||
adder(name, result);
|
var response = await httpClient.GetAsync(_url).ConfigureAwait(false);
|
||||||
}
|
return await _checkFunc(response);
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
catch (Exception ex)
|
||||||
adder(name, HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}"));
|
{
|
||||||
|
var data = new Dictionary<string, object> { { "url", _url } };
|
||||||
|
return HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}", data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,8 +51,7 @@ namespace Microsoft.Extensions.HealthChecks.Internal
|
|||||||
|
|
||||||
public static async ValueTask<IHealthCheckResult> DefaultUrlCheck(HttpResponseMessage response)
|
public static async ValueTask<IHealthCheckResult> DefaultUrlCheck(HttpResponseMessage response)
|
||||||
{
|
{
|
||||||
// REVIEW: Should this be an explicit 200 check, or just an "is success" check?
|
var status = response.IsSuccessStatusCode ? CheckStatus.Healthy : CheckStatus.Unhealthy;
|
||||||
var status = response.StatusCode == HttpStatusCode.OK ? CheckStatus.Healthy : CheckStatus.Unhealthy;
|
|
||||||
var data = new Dictionary<string, object>
|
var data = new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
{ "url", response.RequestMessage.RequestUri.ToString() },
|
{ "url", response.RequestMessage.RequestUri.ToString() },
|
||||||
@ -83,7 +59,7 @@ namespace Microsoft.Extensions.HealthChecks.Internal
|
|||||||
{ "reason", response.ReasonPhrase },
|
{ "reason", response.ReasonPhrase },
|
||||||
{ "body", await response.Content?.ReadAsStringAsync() }
|
{ "body", await response.Content?.ReadAsStringAsync() }
|
||||||
};
|
};
|
||||||
return HealthCheckResult.FromStatus(status, $"UrlCheck({response.RequestMessage.RequestUri}): status code {response.StatusCode} ({(int)response.StatusCode})", data);
|
return HealthCheckResult.FromStatus(status, $"status code {response.StatusCode} ({(int)response.StatusCode})", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual HttpClient GetHttpClient()
|
protected virtual HttpClient GetHttpClient()
|
||||||
|
@ -9,37 +9,49 @@ static class Guard
|
|||||||
public static void ArgumentNotNull(string argumentName, object value)
|
public static void ArgumentNotNull(string argumentName, object value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
|
{
|
||||||
throw new ArgumentNullException(argumentName);
|
throw new ArgumentNullException(argumentName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ArgumentNotNullOrEmpty<T>(string argumentName, string value)
|
public static void ArgumentNotNullOrEmpty(string argumentName, string value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
|
{
|
||||||
throw new ArgumentNullException(argumentName);
|
throw new ArgumentNullException(argumentName);
|
||||||
|
}
|
||||||
if (string.IsNullOrEmpty(value))
|
if (string.IsNullOrEmpty(value))
|
||||||
throw new ArgumentException("Value cannot be an empty string", argumentName);
|
{
|
||||||
|
throw new ArgumentException("Value cannot be an empty string.", argumentName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use IReadOnlyCollection<T> instead of IEnumerable<T> to discourage double enumeration
|
// Use IReadOnlyCollection<T> instead of IEnumerable<T> to discourage double enumeration
|
||||||
public static void ArgumentNotNullOrEmpty<T>(string argumentName, IReadOnlyCollection<T> items)
|
public static void ArgumentNotNullOrEmpty<T>(string argumentName, IReadOnlyCollection<T> items)
|
||||||
{
|
{
|
||||||
if (items == null)
|
if (items == null)
|
||||||
|
{
|
||||||
throw new ArgumentNullException(argumentName);
|
throw new ArgumentNullException(argumentName);
|
||||||
|
}
|
||||||
if (items.Count == 0)
|
if (items.Count == 0)
|
||||||
throw new ArgumentException("Collection must contain at least one item", argumentName);
|
{
|
||||||
}
|
throw new ArgumentException("Collection must contain at least one item.", argumentName);
|
||||||
|
}
|
||||||
public static void ArgumentNotNullOrWhitespace(string argumentName, string value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
throw new ArgumentNullException(argumentName);
|
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
|
||||||
throw new ArgumentException("Value must contain a non-whitespace value", argumentName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ArgumentValid(bool valid, string argumentName, string exceptionMessage)
|
public static void ArgumentValid(bool valid, string argumentName, string exceptionMessage)
|
||||||
{
|
{
|
||||||
if (!valid)
|
if (!valid)
|
||||||
|
{
|
||||||
throw new ArgumentException(exceptionMessage, argumentName);
|
throw new ArgumentException(exceptionMessage, argumentName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OperationValid(bool valid, string exceptionMessage)
|
||||||
|
{
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(exceptionMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,7 @@ namespace eShopOnContainers
|
|||||||
private void InitApp()
|
private void InitApp()
|
||||||
{
|
{
|
||||||
UseMockServices = Settings.UseMocks;
|
UseMockServices = Settings.UseMocks;
|
||||||
ViewModelLocator.Initialize();
|
ViewModelLocator.RegisterDependencies(UseMockServices);
|
||||||
ViewModelLocator.UpdateDependencies(UseMockServices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task InitNavigation()
|
private Task InitNavigation()
|
||||||
|
@ -31,7 +31,7 @@ namespace eShopOnContainers.Core.Behaviors
|
|||||||
BindableProperty.CreateAttached("EventArgsConverterParameter", typeof(object), typeof(EventToCommandBehavior), null,
|
BindableProperty.CreateAttached("EventArgsConverterParameter", typeof(object), typeof(EventToCommandBehavior), null,
|
||||||
BindingMode.OneWay);
|
BindingMode.OneWay);
|
||||||
|
|
||||||
private Delegate _handler;
|
protected Delegate _handler;
|
||||||
private EventInfo _eventInfo;
|
private EventInfo _eventInfo;
|
||||||
|
|
||||||
public string EventName
|
public string EventName
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using eShopOnContainers.Core.ViewModels.Base;
|
|
||||||
using Plugin.Settings;
|
using Plugin.Settings;
|
||||||
using Plugin.Settings.Abstractions;
|
using Plugin.Settings.Abstractions;
|
||||||
|
|
||||||
@ -32,7 +31,6 @@ namespace eShopOnContainers.Core.Helpers
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
public static string AuthAccessToken
|
public static string AuthAccessToken
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -57,7 +55,6 @@ namespace eShopOnContainers.Core.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static bool UseMocks
|
public static bool UseMocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -16,8 +16,7 @@ namespace eShopOnContainers.Core.Services.Basket
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CustomerBasket> GetBasketAsync(string guidUser, string token)
|
public async Task<CustomerBasket> GetBasketAsync(string guidUser, string token)
|
||||||
{
|
{
|
||||||
|
|
||||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);
|
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);
|
||||||
|
|
||||||
builder.Path = guidUser;
|
builder.Path = guidUser;
|
||||||
@ -30,18 +29,17 @@ namespace eShopOnContainers.Core.Services.Basket
|
|||||||
ServicesHelper.FixBasketItemPictureUri(basket?.Items);
|
ServicesHelper.FixBasketItemPictureUri(basket?.Items);
|
||||||
|
|
||||||
return basket;
|
return basket;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CustomerBasket> UpdateBasketAsync(CustomerBasket customerBasket, string token)
|
public async Task<CustomerBasket> UpdateBasketAsync(CustomerBasket customerBasket, string token)
|
||||||
{
|
{
|
||||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);
|
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);
|
||||||
|
|
||||||
string uri = builder.ToString();
|
string uri = builder.ToString();
|
||||||
|
|
||||||
var result = await _requestProvider.PostAsync(uri, customerBasket, token);
|
var result = await _requestProvider.PostAsync(uri, customerBasket, token);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ClearBasketAsync(string guidUser, string token)
|
public async Task ClearBasketAsync(string guidUser, string token)
|
||||||
|
@ -61,12 +61,5 @@ namespace eShopOnContainers.Core.Services.Catalog
|
|||||||
|
|
||||||
return MockCatalogType;
|
return MockCatalogType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CatalogItem> GetCatalogItemAsync(string id)
|
|
||||||
{
|
|
||||||
await Task.Delay(500);
|
|
||||||
|
|
||||||
return MockCatalog.FirstOrDefault(c => c.Id == id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,7 +20,6 @@ namespace eShopOnContainers.Core.Services.Catalog
|
|||||||
|
|
||||||
public async Task<ObservableCollection<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId)
|
public async Task<ObservableCollection<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId)
|
||||||
{
|
{
|
||||||
|
|
||||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
|
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
|
||||||
|
|
||||||
builder.Path = string.Format("api/v1/catalog/items/type/{0}/brand/{1}", catalogTypeId, catalogBrandId);
|
builder.Path = string.Format("api/v1/catalog/items/type/{0}/brand/{1}", catalogTypeId, catalogBrandId);
|
||||||
@ -34,12 +33,10 @@ namespace eShopOnContainers.Core.Services.Catalog
|
|||||||
return catalog?.Data.ToObservableCollection();
|
return catalog?.Data.ToObservableCollection();
|
||||||
else
|
else
|
||||||
return new ObservableCollection<CatalogItem>();
|
return new ObservableCollection<CatalogItem>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ObservableCollection<CatalogItem>> GetCatalogAsync()
|
public async Task<ObservableCollection<CatalogItem>> GetCatalogAsync()
|
||||||
{
|
{
|
||||||
|
|
||||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
|
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
|
||||||
|
|
||||||
builder.Path = "api/v1/catalog/items";
|
builder.Path = "api/v1/catalog/items";
|
||||||
@ -56,37 +53,28 @@ namespace eShopOnContainers.Core.Services.Catalog
|
|||||||
return catalog?.Data.ToObservableCollection();
|
return catalog?.Data.ToObservableCollection();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return new ObservableCollection<CatalogItem>();
|
return new ObservableCollection<CatalogItem>();
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<CatalogItem> GetCatalogItemAsync(string id)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ObservableCollection<CatalogBrand>> GetCatalogBrandAsync()
|
public async Task<ObservableCollection<CatalogBrand>> GetCatalogBrandAsync()
|
||||||
{
|
{
|
||||||
|
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
|
||||||
|
|
||||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
|
builder.Path = "api/v1/catalog/catalogbrands";
|
||||||
|
|
||||||
builder.Path = "api/v1/catalog/catalogbrands";
|
string uri = builder.ToString();
|
||||||
|
|
||||||
string uri = builder.ToString();
|
IEnumerable<CatalogBrand> brands =
|
||||||
|
await _requestProvider.GetAsync<IEnumerable<CatalogBrand>>(uri);
|
||||||
IEnumerable<CatalogBrand> brands =
|
|
||||||
await _requestProvider.GetAsync<IEnumerable<CatalogBrand>>(uri);
|
|
||||||
|
|
||||||
if (brands != null)
|
|
||||||
return brands?.ToObservableCollection();
|
|
||||||
else
|
|
||||||
return new ObservableCollection<CatalogBrand>();
|
|
||||||
|
|
||||||
|
if (brands != null)
|
||||||
|
return brands?.ToObservableCollection();
|
||||||
|
else
|
||||||
|
return new ObservableCollection<CatalogBrand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ObservableCollection<CatalogType>> GetCatalogTypeAsync()
|
public async Task<ObservableCollection<CatalogType>> GetCatalogTypeAsync()
|
||||||
{
|
{
|
||||||
|
|
||||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
|
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
|
||||||
|
|
||||||
builder.Path = "api/v1/catalog/catalogtypes";
|
builder.Path = "api/v1/catalog/catalogtypes";
|
||||||
@ -99,8 +87,7 @@ namespace eShopOnContainers.Core.Services.Catalog
|
|||||||
if (types != null)
|
if (types != null)
|
||||||
return types.ToObservableCollection();
|
return types.ToObservableCollection();
|
||||||
else
|
else
|
||||||
return new ObservableCollection<CatalogType>();
|
return new ObservableCollection<CatalogType>();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,5 @@ namespace eShopOnContainers.Core.Services.Catalog
|
|||||||
Task<ObservableCollection<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId);
|
Task<ObservableCollection<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId);
|
||||||
Task<ObservableCollection<CatalogType>> GetCatalogTypeAsync();
|
Task<ObservableCollection<CatalogType>> GetCatalogTypeAsync();
|
||||||
Task<ObservableCollection<CatalogItem>> GetCatalogAsync();
|
Task<ObservableCollection<CatalogItem>> GetCatalogAsync();
|
||||||
Task<CatalogItem> GetCatalogItemAsync(string id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
using System.Threading.Tasks;
|
namespace eShopOnContainers.Core.Services.Identity
|
||||||
|
|
||||||
namespace eShopOnContainers.Core.Services.Identity
|
|
||||||
{
|
{
|
||||||
public interface IIdentityService
|
public interface IIdentityService
|
||||||
{
|
{
|
||||||
string CreateAuthorizeRequest();
|
string CreateAuthorizationRequest();
|
||||||
string CreateLogoutRequest(string token);
|
string CreateLogoutRequest(string token);
|
||||||
string DecodeToken(string token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,19 +1,15 @@
|
|||||||
using IdentityModel.Client;
|
using IdentityModel.Client;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace eShopOnContainers.Core.Services.Identity
|
namespace eShopOnContainers.Core.Services.Identity
|
||||||
{
|
{
|
||||||
public class IdentityService : IIdentityService
|
public class IdentityService : IIdentityService
|
||||||
{
|
{
|
||||||
public string CreateAuthorizeRequest()
|
public string CreateAuthorizationRequest()
|
||||||
{
|
{
|
||||||
// Create URI to authorize endpoint
|
// Create URI to authorization endpoint
|
||||||
var authorizeRequest =
|
var authorizeRequest = new AuthorizeRequest(GlobalSetting.Instance.IdentityEndpoint);
|
||||||
new AuthorizeRequest(GlobalSetting.Instance.IdentityEndpoint);
|
|
||||||
|
|
||||||
// Dictionary with values for the authorize request
|
// Dictionary with values for the authorize request
|
||||||
var dic = new Dictionary<string, string>();
|
var dic = new Dictionary<string, string>();
|
||||||
@ -29,7 +25,6 @@ namespace eShopOnContainers.Core.Services.Identity
|
|||||||
dic.Add("state", currentCSRFToken);
|
dic.Add("state", currentCSRFToken);
|
||||||
|
|
||||||
var authorizeUri = authorizeRequest.Create(dic);
|
var authorizeUri = authorizeRequest.Create(dic);
|
||||||
|
|
||||||
return authorizeUri;
|
return authorizeUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,30 +40,5 @@ namespace eShopOnContainers.Core.Services.Identity
|
|||||||
token,
|
token,
|
||||||
GlobalSetting.Instance.LogoutCallback);
|
GlobalSetting.Instance.LogoutCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DecodeToken(string token)
|
|
||||||
{
|
|
||||||
var parts = token.Split('.');
|
|
||||||
|
|
||||||
string partToConvert = parts[1];
|
|
||||||
partToConvert = partToConvert.Replace('-', '+');
|
|
||||||
partToConvert = partToConvert.Replace('_', '/');
|
|
||||||
switch (partToConvert.Length % 4)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
partToConvert += "==";
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
partToConvert += "=";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var partAsBytes = Convert.FromBase64String(partToConvert);
|
|
||||||
var partAsUTF8String = Encoding.UTF8.GetString(partAsBytes, 0, partAsBytes.Count());
|
|
||||||
|
|
||||||
return JObject.Parse(partAsUTF8String).ToString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,8 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
{
|
{
|
||||||
Task<TResult> GetAsync<TResult>(string uri, string token = "");
|
Task<TResult> GetAsync<TResult>(string uri, string token = "");
|
||||||
|
|
||||||
Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "");
|
|
||||||
|
|
||||||
Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "", string header = "");
|
Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "", string header = "");
|
||||||
|
|
||||||
Task<TResult> PostAsync<TRequest, TResult>(string uri, TRequest data, string token = "");
|
|
||||||
|
|
||||||
Task<TResult> PutAsync<TResult>(string uri, TResult data, string token = "");
|
|
||||||
|
|
||||||
Task<TResult> PutAsync<TRequest, TResult>(string uri, TRequest data, string token = "");
|
|
||||||
|
|
||||||
Task DeleteAsync(string uri, string token = "");
|
Task DeleteAsync(string uri, string token = "");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,6 @@ using Newtonsoft.Json.Serialization;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -23,7 +22,6 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
};
|
};
|
||||||
|
|
||||||
_serializerSettings.Converters.Add(new StringEnumConverter());
|
_serializerSettings.Converters.Add(new StringEnumConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +31,6 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
HttpResponseMessage response = await httpClient.GetAsync(uri);
|
HttpResponseMessage response = await httpClient.GetAsync(uri);
|
||||||
|
|
||||||
await HandleResponse(response);
|
await HandleResponse(response);
|
||||||
|
|
||||||
string serialized = await response.Content.ReadAsStringAsync();
|
string serialized = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
TResult result = await Task.Run(() =>
|
TResult result = await Task.Run(() =>
|
||||||
@ -56,7 +53,6 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
|
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
|
||||||
|
|
||||||
await HandleResponse(response);
|
await HandleResponse(response);
|
||||||
|
|
||||||
string serialized = await response.Content.ReadAsStringAsync();
|
string serialized = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
TResult result = await Task.Run(() =>
|
TResult result = await Task.Run(() =>
|
||||||
@ -65,61 +61,21 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "")
|
|
||||||
{
|
|
||||||
return PostAsync<TResult, TResult>(uri, data, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TResult> PostAsync<TRequest, TResult>(string uri, TRequest data, string token = "")
|
|
||||||
{
|
|
||||||
HttpClient httpClient = CreateHttpClient(token);
|
|
||||||
string serialized = await Task.Run(() => JsonConvert.SerializeObject(data, _serializerSettings));
|
|
||||||
var content = new StringContent(serialized, Encoding.UTF8, "application/json");
|
|
||||||
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
|
|
||||||
|
|
||||||
await HandleResponse(response);
|
|
||||||
|
|
||||||
string responseData = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
return await Task.Run(() => JsonConvert.DeserializeObject<TResult>(responseData, _serializerSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<TResult> PutAsync<TResult>(string uri, TResult data, string token = "")
|
|
||||||
{
|
|
||||||
return PutAsync<TResult, TResult>(uri, data, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TResult> PutAsync<TRequest, TResult>(string uri, TRequest data, string token = "")
|
|
||||||
{
|
|
||||||
HttpClient httpClient = CreateHttpClient(token);
|
|
||||||
string serialized = await Task.Run(() => JsonConvert.SerializeObject(data, _serializerSettings));
|
|
||||||
HttpResponseMessage response = await httpClient.PutAsync(uri, new StringContent(serialized, Encoding.UTF8, "application/json"));
|
|
||||||
|
|
||||||
await HandleResponse(response);
|
|
||||||
|
|
||||||
string responseData = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
return await Task.Run(() => JsonConvert.DeserializeObject<TResult>(responseData, _serializerSettings));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteAsync(string uri, string token = "")
|
public async Task DeleteAsync(string uri, string token = "")
|
||||||
{
|
{
|
||||||
HttpClient httpClient = CreateHttpClient(token);
|
HttpClient httpClient = CreateHttpClient(token);
|
||||||
|
|
||||||
await httpClient.DeleteAsync(uri);
|
await httpClient.DeleteAsync(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpClient CreateHttpClient(string token = "")
|
private HttpClient CreateHttpClient(string token = "")
|
||||||
{
|
{
|
||||||
var httpClient = new HttpClient();
|
var httpClient = new HttpClient();
|
||||||
|
|
||||||
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(token))
|
if (!string.IsNullOrEmpty(token))
|
||||||
{
|
{
|
||||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpClient;
|
return httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,8 +96,8 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
{
|
{
|
||||||
var content = await response.Content.ReadAsStringAsync();
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
if (response.StatusCode == HttpStatusCode.Forbidden
|
if (response.StatusCode == HttpStatusCode.Forbidden ||
|
||||||
|| response.StatusCode == HttpStatusCode.Unauthorized)
|
response.StatusCode == HttpStatusCode.Unauthorized)
|
||||||
{
|
{
|
||||||
throw new ServiceAuthenticationException(content);
|
throw new ServiceAuthenticationException(content);
|
||||||
}
|
}
|
||||||
|
70
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs
Normal file → Executable file
70
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs
Normal file → Executable file
@ -1,4 +1,4 @@
|
|||||||
using Microsoft.Practices.Unity;
|
using Autofac;
|
||||||
using eShopOnContainers.Services;
|
using eShopOnContainers.Services;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -16,7 +16,7 @@ namespace eShopOnContainers.Core.ViewModels.Base
|
|||||||
{
|
{
|
||||||
public static class ViewModelLocator
|
public static class ViewModelLocator
|
||||||
{
|
{
|
||||||
private static readonly IUnityContainer _unityContainer = new UnityContainer();
|
private static IContainer _container;
|
||||||
|
|
||||||
public static readonly BindableProperty AutoWireViewModelProperty =
|
public static readonly BindableProperty AutoWireViewModelProperty =
|
||||||
BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged);
|
BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged);
|
||||||
@ -33,56 +33,56 @@ namespace eShopOnContainers.Core.ViewModels.Base
|
|||||||
|
|
||||||
public static bool UseMockService { get; set; }
|
public static bool UseMockService { get; set; }
|
||||||
|
|
||||||
public static void Initialize()
|
public static void RegisterDependencies(bool useMockServices)
|
||||||
{
|
{
|
||||||
// Services
|
var builder = new ContainerBuilder();
|
||||||
_unityContainer.RegisterType<IDialogService, DialogService>();
|
|
||||||
_unityContainer.RegisterType<INavigationService, NavigationService>(new ContainerControlledLifetimeManager());
|
|
||||||
_unityContainer.RegisterType<IOpenUrlService, OpenUrlService>();
|
|
||||||
_unityContainer.RegisterType<IRequestProvider, RequestProvider>();
|
|
||||||
_unityContainer.RegisterType<IIdentityService, IdentityService>();
|
|
||||||
_unityContainer.RegisterType<ICatalogService, CatalogMockService>();
|
|
||||||
_unityContainer.RegisterType<IBasketService, BasketMockService>();
|
|
||||||
_unityContainer.RegisterType<IUserService, UserMockService>();
|
|
||||||
|
|
||||||
// View models
|
// View models
|
||||||
_unityContainer.RegisterType<BasketViewModel>();
|
builder.RegisterType<BasketViewModel>();
|
||||||
_unityContainer.RegisterType<CatalogViewModel>();
|
builder.RegisterType<CatalogViewModel>();
|
||||||
_unityContainer.RegisterType<CheckoutViewModel>();
|
builder.RegisterType<CheckoutViewModel>();
|
||||||
_unityContainer.RegisterType<LoginViewModel>();
|
builder.RegisterType<LoginViewModel>();
|
||||||
_unityContainer.RegisterType<MainViewModel>();
|
builder.RegisterType<MainViewModel>();
|
||||||
_unityContainer.RegisterType<OrderDetailViewModel>();
|
builder.RegisterType<OrderDetailViewModel>();
|
||||||
_unityContainer.RegisterType<ProfileViewModel>();
|
builder.RegisterType<ProfileViewModel>();
|
||||||
_unityContainer.RegisterType<SettingsViewModel>();
|
builder.RegisterType<SettingsViewModel>();
|
||||||
}
|
|
||||||
|
// Services
|
||||||
|
builder.RegisterType<NavigationService>().As<INavigationService>().SingleInstance();
|
||||||
|
builder.RegisterType<DialogService>().As<IDialogService>();
|
||||||
|
builder.RegisterType<OpenUrlService>().As<IOpenUrlService>();
|
||||||
|
builder.RegisterType<IdentityService>().As<IIdentityService>();
|
||||||
|
builder.RegisterType<RequestProvider>().As<IRequestProvider>();
|
||||||
|
|
||||||
public static void UpdateDependencies(bool useMockServices)
|
|
||||||
{
|
|
||||||
// Change injected dependencies
|
|
||||||
if (useMockServices)
|
if (useMockServices)
|
||||||
{
|
{
|
||||||
_unityContainer.RegisterInstance<ICatalogService>(new CatalogMockService());
|
builder.RegisterInstance(new CatalogMockService()).As<ICatalogService>();
|
||||||
_unityContainer.RegisterInstance<IBasketService>(new BasketMockService());
|
builder.RegisterInstance(new BasketMockService()).As<IBasketService>();
|
||||||
_unityContainer.RegisterInstance<IOrderService>(new OrderMockService());
|
builder.RegisterInstance(new OrderMockService()).As<IOrderService>();
|
||||||
_unityContainer.RegisterInstance<IUserService>(new UserMockService());
|
builder.RegisterInstance(new UserMockService()).As<IUserService>();
|
||||||
|
|
||||||
UseMockService = true;
|
UseMockService = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var requestProvider = Resolve<IRequestProvider>();
|
builder.RegisterType<CatalogService>().As<ICatalogService>().SingleInstance();
|
||||||
_unityContainer.RegisterInstance<ICatalogService>(new CatalogService(requestProvider));
|
builder.RegisterType<BasketService>().As<IBasketService>().SingleInstance();
|
||||||
_unityContainer.RegisterInstance<IBasketService>(new BasketService(requestProvider));
|
builder.RegisterType<OrderService>().As<IOrderService>().SingleInstance();
|
||||||
_unityContainer.RegisterInstance<IOrderService>(new OrderService(requestProvider));
|
builder.RegisterType<UserService>().As<IUserService>().SingleInstance();
|
||||||
_unityContainer.RegisterInstance<IUserService>(new UserService(requestProvider));
|
|
||||||
|
|
||||||
UseMockService = false;
|
UseMockService = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_container != null)
|
||||||
|
{
|
||||||
|
_container.Dispose();
|
||||||
|
}
|
||||||
|
_container = builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Resolve<T>()
|
public static T Resolve<T>()
|
||||||
{
|
{
|
||||||
return _unityContainer.Resolve<T>();
|
return _container.Resolve<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
|
private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
|
||||||
@ -103,7 +103,7 @@ namespace eShopOnContainers.Core.ViewModels.Base
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var viewModel = _unityContainer.Resolve(viewModelType);
|
var viewModel = _container.Resolve(viewModelType);
|
||||||
view.BindingContext = viewModel;
|
view.BindingContext = viewModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ using Xamarin.Forms;
|
|||||||
using eShopOnContainers.Core.Models.Catalog;
|
using eShopOnContainers.Core.Models.Catalog;
|
||||||
using eShopOnContainers.Core.Services.Catalog;
|
using eShopOnContainers.Core.Services.Catalog;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using eShopOnContainers.Core.Services.Basket;
|
|
||||||
using eShopOnContainers.Core.Services.User;
|
|
||||||
|
|
||||||
namespace eShopOnContainers.Core.ViewModels
|
namespace eShopOnContainers.Core.ViewModels
|
||||||
{
|
{
|
||||||
|
@ -188,7 +188,7 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
LoginUrl = _identityService.CreateAuthorizeRequest();
|
LoginUrl = _identityService.CreateAuthorizationRequest();
|
||||||
|
|
||||||
IsValid = true;
|
IsValid = true;
|
||||||
IsLogin = true;
|
IsLogin = true;
|
||||||
@ -228,7 +228,7 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
Settings.AuthAccessToken = string.Empty;
|
Settings.AuthAccessToken = string.Empty;
|
||||||
Settings.AuthIdToken = string.Empty;
|
Settings.AuthIdToken = string.Empty;
|
||||||
IsLogin = false;
|
IsLogin = false;
|
||||||
LoginUrl = _identityService.CreateAuthorizeRequest();
|
LoginUrl = _identityService.CreateAuthorizationRequest();
|
||||||
}
|
}
|
||||||
else if (unescapedUrl.Contains(GlobalSetting.Instance.IdentityCallback))
|
else if (unescapedUrl.Contains(GlobalSetting.Instance.IdentityCallback))
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using eShopOnContainers.Core.Models.Orders;
|
using eShopOnContainers.Core.Models.Orders;
|
||||||
using eShopOnContainers.Core.ViewModels.Base;
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
using eShopOnContainers.Core.Services.Catalog;
|
|
||||||
using eShopOnContainers.Core.Services.Basket;
|
|
||||||
using eShopOnContainers.Core.Services.Order;
|
using eShopOnContainers.Core.Services.Order;
|
||||||
using System;
|
using System;
|
||||||
using eShopOnContainers.Core.Helpers;
|
using eShopOnContainers.Core.Helpers;
|
||||||
@ -11,9 +9,8 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
{
|
{
|
||||||
public class OrderDetailViewModel : ViewModelBase
|
public class OrderDetailViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private Order _order;
|
|
||||||
|
|
||||||
private IOrderService _ordersService;
|
private IOrderService _ordersService;
|
||||||
|
private Order _order;
|
||||||
|
|
||||||
public OrderDetailViewModel(IOrderService ordersService)
|
public OrderDetailViewModel(IOrderService ordersService)
|
||||||
{
|
{
|
||||||
|
@ -71,16 +71,14 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
|
|
||||||
private void MockServices()
|
private void MockServices()
|
||||||
{
|
{
|
||||||
ViewModelLocator.UpdateDependencies(!UseAzureServices);
|
ViewModelLocator.RegisterDependencies(!UseAzureServices);
|
||||||
UpdateInfo();
|
UpdateInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task InitializeAsync(object navigationData)
|
public override Task InitializeAsync(object navigationData)
|
||||||
{
|
{
|
||||||
UpdateInfo();
|
UpdateInfo();
|
||||||
|
|
||||||
Endpoint = Settings.UrlBase;
|
Endpoint = Settings.UrlBase;
|
||||||
|
|
||||||
return base.InitializeAsync(navigationData);
|
return base.InitializeAsync(navigationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,12 +87,12 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
if (!UseAzureServices)
|
if (!UseAzureServices)
|
||||||
{
|
{
|
||||||
Title = "Use Mock Services";
|
Title = "Use Mock Services";
|
||||||
Description = "Mock Services are simulated objects that mimic the behavior of real services in controlled ways";
|
Description = "Mock Services are simulated objects that mimic the behavior of real services using a controlled approach.";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Title = "Use Microservices/Containers from eShopOnContainers";
|
Title = "Use Microservices/Containers from eShopOnContainers";
|
||||||
Description = "When enabling the use of microservices/containers the Xamarin.Forms app will try to use real services deployed as Docker containers in the specified base IP that will need to be reachable through the network";
|
Description = "When enabling the use of microservices/containers, the app will attempt to use real services deployed as Docker containers at the specified base endpoint, which will must be reachable through the network.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@
|
|||||||
<RootNamespace>eShopOnContainers.Core</RootNamespace>
|
<RootNamespace>eShopOnContainers.Core</RootNamespace>
|
||||||
<AssemblyName>eShopOnContainers.Core</AssemblyName>
|
<AssemblyName>eShopOnContainers.Core</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
|
<TargetFrameworkProfile>Profile111</TargetFrameworkProfile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -170,85 +170,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
<None Include="packages.config" />
|
<None Include="project.json" />
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Acr.UserDialogs, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Acr.UserDialogs.6.3.3\lib\portable-win+net45+wp8+win8+wpa81\Acr.UserDialogs.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Acr.UserDialogs.Interface, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Acr.UserDialogs.6.3.3\lib\portable-win+net45+wp8+win8+wpa81\Acr.UserDialogs.Interface.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FFImageLoading, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xamarin.FFImageLoading.2.2.6-pre-256\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FFImageLoading.Forms, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.2.6-pre-256\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.Forms.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FFImageLoading.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xamarin.FFImageLoading.2.2.6-pre-256\lib\portable-net45+win8+wpa81+wp8\FFImageLoading.Platform.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="IdentityModel.Portable, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\IdentityModel.1.3.1\lib\portable-net45+wp80+win8+wpa81\IdentityModel.Portable.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Microsoft.Practices.Unity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=6d32ff45e0ccc69f, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Unity.4.0.1\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Newtonsoft.Json.9.0.2-beta1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Settings, Version=2.6.0.12, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugins.Settings.2.6.0.12-beta\lib\portable-net45+wp80+win8+wpa81\Plugin.Settings.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Settings.Abstractions, Version=2.6.0.12, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugins.Settings.2.6.0.12-beta\lib\portable-net45+wp80+win8+wpa81\Plugin.Settings.Abstractions.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SlideOverKit, Version=1.0.6135.18790, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.4\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SlideOverKit.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Splat.1.6.2\lib\Portable-net45+win+wpa81+wp80\Splat.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Net.Http, Version=1.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Net.Http.Extensions, Version=1.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.Extensions.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Net.Http.Primitives, Version=1.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.Primitives.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xamarin.Forms.2.3.3.175\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Core.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Forms.Pages, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xamarin.Forms.Pages.2.3.3.175\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10\Xamarin.Forms.Pages.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Forms.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xamarin.Forms.2.3.3.175\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Platform.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Xamarin.Forms.Xaml, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Xamarin.Forms.2.3.3.175\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Xaml.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Controls\AddBasketButton.xaml">
|
<EmbeddedResource Include="Controls\AddBasketButton.xaml">
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<packages>
|
|
||||||
<package id="Acr.UserDialogs" version="6.3.3" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="CommonServiceLocator" version="1.3" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="IdentityModel" version="1.3.1" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="modernhttpclient" version="2.4.2" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Newtonsoft.Json" version="9.0.2-beta1" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="SlideOverKit" version="2.1.4" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Splat" version="1.6.2" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Unity" version="4.0.1" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Xam.Plugins.Settings" version="2.6.0.12-beta" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Xamarin.FFImageLoading" version="2.2.6-pre-256" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Xamarin.FFImageLoading.Forms" version="2.2.6-pre-256" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Xamarin.Forms" version="2.3.3.175" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Xamarin.Forms.Pages" version="2.3.3.175" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Xamarin.Forms.Theme.Base" version="1.0.0.43-pre1" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
<package id="Xamarin.Forms.Theme.Light" version="1.0.0.43-pre1" targetFramework="portable45-net45+win8+wp8+wpa81" />
|
|
||||||
</packages>
|
|
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"Acr.UserDialogs": "6.3.3",
|
||||||
|
"Autofac": "4.4.0",
|
||||||
|
"CommonServiceLocator": "1.3",
|
||||||
|
"IdentityModel": "1.3.1",
|
||||||
|
"Microsoft.Bcl": "1.1.10",
|
||||||
|
"Microsoft.Bcl.Build": "1.0.21",
|
||||||
|
"Microsoft.Net.Http": "2.2.29",
|
||||||
|
"modernhttpclient": "2.4.2",
|
||||||
|
"Newtonsoft.Json": "9.0.2-beta1",
|
||||||
|
"SlideOverKit": "2.1.4",
|
||||||
|
"Splat": "1.6.2",
|
||||||
|
"Xam.Plugins.Settings": "2.6.0.12-beta",
|
||||||
|
"Xamarin.FFImageLoading": "2.2.6-pre-256",
|
||||||
|
"Xamarin.FFImageLoading.Forms": "2.2.6-pre-256",
|
||||||
|
"Xamarin.Forms": "2.3.3.175",
|
||||||
|
"Xamarin.Forms.Pages": "2.3.3.175",
|
||||||
|
"Xamarin.Forms.Theme.Base": "1.0.0.43-pre1",
|
||||||
|
"Xamarin.Forms.Theme.Light": "1.0.0.43-pre1"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
".NETPortable,Version=v4.5,Profile=Profile111": {}
|
||||||
|
}
|
||||||
|
}
|
@ -21,10 +21,11 @@
|
|||||||
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
|
||||||
<AndroidStoreUncompressedFileExtensions />
|
<AndroidStoreUncompressedFileExtensions />
|
||||||
<MandroidI18n />
|
<MandroidI18n />
|
||||||
<JavaMaximumHeapSize />
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
<JavaOptions />
|
<JavaOptions />
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
|
<AndroidTlsProvider></AndroidTlsProvider>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>True</DebugSymbols>
|
<DebugSymbols>True</DebugSymbols>
|
||||||
@ -37,14 +38,8 @@
|
|||||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
||||||
<AndroidLinkMode>None</AndroidLinkMode>
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
|
||||||
<BundleAssemblies>False</BundleAssemblies>
|
|
||||||
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
|
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
<Debugger>Xamarin</Debugger>
|
<Debugger>Xamarin</Debugger>
|
||||||
<AotAssemblies>False</AotAssemblies>
|
|
||||||
<EnableLLVM>False</EnableLLVM>
|
|
||||||
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
|
|
||||||
<EnableProguard>False</EnableProguard>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@ -102,10 +97,6 @@
|
|||||||
<HintPath>..\..\..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
|
<HintPath>..\..\..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Microsoft.Practices.Unity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=6d32ff45e0ccc69f, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Unity.4.0.1\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ModernHttpClient, Version=2.4.2.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="ModernHttpClient, Version=2.4.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\..\..\packages\modernhttpclient.2.4.2\lib\MonoAndroid\ModernHttpClient.dll</HintPath>
|
<HintPath>..\..\..\..\packages\modernhttpclient.2.4.2\lib\MonoAndroid\ModernHttpClient.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
<package id="System.Threading.Tasks" version="4.0.11" targetFramework="monoandroid70" />
|
<package id="System.Threading.Tasks" version="4.0.11" targetFramework="monoandroid70" />
|
||||||
<package id="System.Xml.ReaderWriter" version="4.0.11" targetFramework="monoandroid70" />
|
<package id="System.Xml.ReaderWriter" version="4.0.11" targetFramework="monoandroid70" />
|
||||||
<package id="System.Xml.XDocument" version="4.0.11" targetFramework="monoandroid70" />
|
<package id="System.Xml.XDocument" version="4.0.11" targetFramework="monoandroid70" />
|
||||||
<package id="Unity" version="4.0.1" targetFramework="monoandroid70" />
|
|
||||||
<package id="Xam.Plugins.Settings" version="2.6.0.12-beta" targetFramework="monoandroid70" />
|
<package id="Xam.Plugins.Settings" version="2.6.0.12-beta" targetFramework="monoandroid70" />
|
||||||
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="23.3.0" targetFramework="monoandroid70" />
|
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="23.3.0" targetFramework="monoandroid70" />
|
||||||
<package id="Xamarin.Android.Support.Design" version="23.3.0" targetFramework="monoandroid70" />
|
<package id="Xamarin.Android.Support.Design" version="23.3.0" targetFramework="monoandroid70" />
|
||||||
|
@ -14,7 +14,7 @@ namespace eShopOnContainers.TestRunner.Droid
|
|||||||
AddExecutionAssembly(typeof(ExtensibilityPointFactory).Assembly);
|
AddExecutionAssembly(typeof(ExtensibilityPointFactory).Assembly);
|
||||||
|
|
||||||
// or in any reference assemblies getting the Assembly from any type/class
|
// or in any reference assemblies getting the Assembly from any type/class
|
||||||
AddTestAssembly(typeof(UnitTests.DummyTests).Assembly);
|
AddTestAssembly(typeof(UnitTests.CatalogViewModelTests).Assembly);
|
||||||
|
|
||||||
base.OnCreate(bundle);
|
base.OnCreate(bundle);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
|
<AndroidTlsProvider></AndroidTlsProvider>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
@ -12,7 +12,7 @@ namespace eShopOnContainers.TestRunner.Windows
|
|||||||
{
|
{
|
||||||
// Otherwise you need to ensure that the test assemblies will
|
// Otherwise you need to ensure that the test assemblies will
|
||||||
// become part of the app bundle
|
// become part of the app bundle
|
||||||
AddTestAssembly(typeof(UnitTests.DummyTests).GetTypeInfo().Assembly);
|
AddTestAssembly(typeof(UnitTests.CatalogViewModelTests).GetTypeInfo().Assembly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,7 +17,7 @@ namespace eShopOnContainers.TestRunner.iOS
|
|||||||
|
|
||||||
// Otherwise you need to ensure that the test assemblies will
|
// Otherwise you need to ensure that the test assemblies will
|
||||||
// become part of the app bundle
|
// become part of the app bundle
|
||||||
AddTestAssembly(typeof(UnitTests.DummyTests).Assembly);
|
AddTestAssembly(typeof(UnitTests.CatalogViewModelTests).Assembly);
|
||||||
|
|
||||||
return base.FinishedLaunching(app, options);
|
return base.FinishedLaunching(app, options);
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,12 @@
|
|||||||
<HintPath>..\..\..\..\packages\xunit.runner.utility.2.2.0-beta4-build3444\lib\netstandard1.1\xunit.runner.utility.dotnet.dll</HintPath>
|
<HintPath>..\..\..\..\packages\xunit.runner.utility.2.2.0-beta4-build3444\lib\netstandard1.1\xunit.runner.utility.dotnet.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Plugin.Settings.Abstractions">
|
||||||
|
<HintPath>..\..\..\..\packages\Xam.Plugins.Settings.2.6.0.12-beta\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Plugin.Settings">
|
||||||
|
<HintPath>..\..\..\..\packages\Xam.Plugins.Settings.2.6.0.12-beta\lib\Xamarin.iOS10\Plugin.Settings.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="AppDelegate.cs.txt" />
|
<Content Include="AppDelegate.cs.txt" />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="CommonServiceLocator" version="1.3" targetFramework="xamarinios10" />
|
<package id="CommonServiceLocator" version="1.3" targetFramework="xamarinios10" />
|
||||||
|
<package id="Xam.Plugins.Settings" version="2.6.0.12-beta" targetFramework="xamarinios10" />
|
||||||
<package id="Xamarin.Forms" version="2.3.3.175" targetFramework="xamarinios10" />
|
<package id="Xamarin.Forms" version="2.3.3.175" targetFramework="xamarinios10" />
|
||||||
<package id="xunit" version="2.2.0-beta4-build3444" targetFramework="xamarinios10" />
|
<package id="xunit" version="2.2.0-beta4-build3444" targetFramework="xamarinios10" />
|
||||||
<package id="xunit.abstractions" version="2.0.1" targetFramework="xamarinios10" />
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="xamarinios10" />
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
using Xunit;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests
|
||||||
|
{
|
||||||
|
public class EventToCommandBehaviorTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void InvalidEventNameShouldThrowArgumentExceptionText()
|
||||||
|
{
|
||||||
|
var behavior = new MockEventToCommandBehavior
|
||||||
|
{
|
||||||
|
EventName = "OnItemTapped"
|
||||||
|
};
|
||||||
|
var listView = new ListView();
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(() => listView.Behaviors.Add(behavior));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CommandExecutedWhenEventFiresText()
|
||||||
|
{
|
||||||
|
bool executedCommand = false;
|
||||||
|
var behavior = new MockEventToCommandBehavior
|
||||||
|
{
|
||||||
|
EventName = "ItemTapped",
|
||||||
|
Command = new Command(() =>
|
||||||
|
{
|
||||||
|
executedCommand = true;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
var listView = new ListView();
|
||||||
|
listView.Behaviors.Add(behavior);
|
||||||
|
|
||||||
|
behavior.RaiseEvent(listView, null);
|
||||||
|
|
||||||
|
Assert.True(executedCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CommandCanExecuteTest()
|
||||||
|
{
|
||||||
|
var behavior = new MockEventToCommandBehavior
|
||||||
|
{
|
||||||
|
EventName = "ItemTapped",
|
||||||
|
Command = new Command(() => Assert.True(false), () => false)
|
||||||
|
};
|
||||||
|
var listView = new ListView();
|
||||||
|
listView.Behaviors.Add(behavior);
|
||||||
|
|
||||||
|
behavior.RaiseEvent(listView, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CommandCanExecuteWithParameterShouldNotExecuteTest()
|
||||||
|
{
|
||||||
|
bool shouldExecute = false;
|
||||||
|
var behavior = new MockEventToCommandBehavior
|
||||||
|
{
|
||||||
|
EventName = "ItemTapped",
|
||||||
|
CommandParameter = shouldExecute,
|
||||||
|
Command = new Command<string>(o => Assert.True(false), o => o.Equals(true))
|
||||||
|
};
|
||||||
|
var listView = new ListView();
|
||||||
|
listView.Behaviors.Add(behavior);
|
||||||
|
|
||||||
|
behavior.RaiseEvent(listView, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CommandWithConverterTest()
|
||||||
|
{
|
||||||
|
const string item = "ItemProperty";
|
||||||
|
bool executedCommand = false;
|
||||||
|
var behavior = new MockEventToCommandBehavior
|
||||||
|
{
|
||||||
|
EventName = "ItemTapped",
|
||||||
|
EventArgsConverter = new ItemTappedEventArgsConverter(false),
|
||||||
|
Command = new Command<string>(o =>
|
||||||
|
{
|
||||||
|
executedCommand = true;
|
||||||
|
Assert.NotNull(o);
|
||||||
|
Assert.Equal(item, o);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
var listView = new ListView();
|
||||||
|
listView.Behaviors.Add(behavior);
|
||||||
|
|
||||||
|
behavior.RaiseEvent(listView, new ItemTappedEventArgs(listView, item));
|
||||||
|
|
||||||
|
Assert.True(executedCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ItemTappedEventArgsConverter : IValueConverter
|
||||||
|
{
|
||||||
|
private readonly bool _returnParameter;
|
||||||
|
|
||||||
|
public bool HasConverted { get; private set; }
|
||||||
|
|
||||||
|
public ItemTappedEventArgsConverter(bool returnParameter)
|
||||||
|
{
|
||||||
|
_returnParameter = returnParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
HasConverted = true;
|
||||||
|
return _returnParameter ? parameter : (value as ItemTappedEventArgs)?.Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace eShopOnContainers.UnitTests
|
|
||||||
{
|
|
||||||
public class DummyTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void ThisShouldPass_Sync()
|
|
||||||
{
|
|
||||||
Assert.True(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ThisShouldPass_Async()
|
|
||||||
{
|
|
||||||
await Task.Run(() => { Assert.True(true); });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ThisShouldFail_Async()
|
|
||||||
{
|
|
||||||
await Task.Run(() => { throw new Exception("Oops!"); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,12 @@
|
|||||||
|
using eShopOnContainers.Core.Behaviors;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests
|
||||||
|
{
|
||||||
|
public class MockEventToCommandBehavior : EventToCommandBehavior
|
||||||
|
{
|
||||||
|
public void RaiseEvent(params object[] args)
|
||||||
|
{
|
||||||
|
_handler.DynamicInvoke(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
|
using eShopOnContainers.Core.Validations;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests
|
||||||
|
{
|
||||||
|
public class MockViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private ValidatableObject<string> _forename;
|
||||||
|
private ValidatableObject<string> _surname;
|
||||||
|
|
||||||
|
public ValidatableObject<string> Forename
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _forename;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_forename = value;
|
||||||
|
RaisePropertyChanged(() => Forename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidatableObject<string> Surname
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _surname;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_surname = value;
|
||||||
|
RaisePropertyChanged(() => Surname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MockViewModel()
|
||||||
|
{
|
||||||
|
_forename = new ValidatableObject<string>();
|
||||||
|
_surname = new ValidatableObject<string>();
|
||||||
|
|
||||||
|
_forename.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "Forename is required." });
|
||||||
|
_surname.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "Surname name is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Validate()
|
||||||
|
{
|
||||||
|
bool isValidForename = _forename.Validate();
|
||||||
|
bool isValidSurname = _surname.Validate();
|
||||||
|
return isValidForename && isValidSurname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
using eShopOnContainers.Core;
|
using eShopOnContainers.Core;
|
||||||
using eShopOnContainers.Core.Services.Order;
|
using eShopOnContainers.Core.Services.Order;
|
||||||
using eShopOnContainers.Core.Services.RequestProvider;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -8,6 +7,15 @@ namespace eShopOnContainers.UnitTests
|
|||||||
{
|
{
|
||||||
public class OrdersServiceTests
|
public class OrdersServiceTests
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task GetFakeOrderTest()
|
||||||
|
{
|
||||||
|
var ordersMockService = new OrderMockService();
|
||||||
|
var order = await ordersMockService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
|
||||||
|
|
||||||
|
Assert.NotNull(order);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetFakeOrdersTest()
|
public async Task GetFakeOrdersTest()
|
||||||
{
|
{
|
||||||
@ -16,15 +24,5 @@ namespace eShopOnContainers.UnitTests
|
|||||||
|
|
||||||
Assert.NotEqual(0, result.Count);
|
Assert.NotEqual(0, result.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetOrdersTest()
|
|
||||||
{
|
|
||||||
var requestProvider = new RequestProvider();
|
|
||||||
var ordersService = new OrderService(requestProvider);
|
|
||||||
var result = await ordersService.GetOrdersAsync(GlobalSetting.Instance.AuthToken);
|
|
||||||
|
|
||||||
Assert.NotEqual(0, result.Count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,223 @@
|
|||||||
|
using Xunit;
|
||||||
|
using eShopOnContainers.Core.ViewModels;
|
||||||
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
|
using eShopOnContainers.Core.Services.Catalog;
|
||||||
|
using eShopOnContainers.Core.Models.Catalog;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests
|
||||||
|
{
|
||||||
|
public class CatalogViewModelTests
|
||||||
|
{
|
||||||
|
public CatalogViewModelTests()
|
||||||
|
{
|
||||||
|
ViewModelLocator.RegisterDependencies(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddCatalogItemCommandIsNotNullTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.NotNull(catalogViewModel.AddCatalogItemCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FilterCommandIsNotNullTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.NotNull(catalogViewModel.FilterCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ClearFilterCommandIsNotNullTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.NotNull(catalogViewModel.ClearFilterCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ProductsPropertyIsNullWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.Null(catalogViewModel.Products);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BrandsPropertyuIsNullWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.Null(catalogViewModel.Brands);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BrandPropertyIsNullWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.Null(catalogViewModel.Brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TypesPropertyIsNullWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.Null(catalogViewModel.Types);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TypePropertyIsNullWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.Null(catalogViewModel.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsFilterPropertyIsFalseWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
Assert.False(catalogViewModel.IsFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProductsPropertyIsNotNullAfterViewModelInitializationTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
|
||||||
|
await catalogViewModel.InitializeAsync(null);
|
||||||
|
|
||||||
|
Assert.NotNull(catalogViewModel.Products);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BrandsPropertyIsNotNullAfterViewModelInitializationTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
|
||||||
|
await catalogViewModel.InitializeAsync(null);
|
||||||
|
|
||||||
|
Assert.NotNull(catalogViewModel.Brands);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TypesPropertyIsNotNullAfterViewModelInitializationTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
|
||||||
|
await catalogViewModel.InitializeAsync(null);
|
||||||
|
|
||||||
|
Assert.NotNull(catalogViewModel.Types);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SettingProductsPropertyShouldRaisePropertyChanged()
|
||||||
|
{
|
||||||
|
bool invoked = false;
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
|
||||||
|
catalogViewModel.PropertyChanged += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName.Equals("Products"))
|
||||||
|
invoked = true;
|
||||||
|
};
|
||||||
|
await catalogViewModel.InitializeAsync(null);
|
||||||
|
|
||||||
|
Assert.True(invoked);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SettingBrandsPropertyShouldRaisePropertyChanged()
|
||||||
|
{
|
||||||
|
bool invoked = false;
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
|
||||||
|
catalogViewModel.PropertyChanged += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName.Equals("Brands"))
|
||||||
|
invoked = true;
|
||||||
|
};
|
||||||
|
await catalogViewModel.InitializeAsync(null);
|
||||||
|
|
||||||
|
Assert.True(invoked);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SettingTypesPropertyShouldRaisePropertyChanged()
|
||||||
|
{
|
||||||
|
bool invoked = false;
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
|
||||||
|
catalogViewModel.PropertyChanged += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName.Equals("Types"))
|
||||||
|
invoked = true;
|
||||||
|
};
|
||||||
|
await catalogViewModel.InitializeAsync(null);
|
||||||
|
|
||||||
|
Assert.True(invoked);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddCatalogItemCommandSendsAddProductMessageTest()
|
||||||
|
{
|
||||||
|
bool messageReceived = false;
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
|
||||||
|
Xamarin.Forms.MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(this, MessageKeys.AddProduct, (sender, arg) =>
|
||||||
|
{
|
||||||
|
messageReceived = true;
|
||||||
|
});
|
||||||
|
catalogViewModel.AddCatalogItemCommand.Execute(null);
|
||||||
|
|
||||||
|
Assert.True(messageReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FilterCommandSendsFilterMessageTest()
|
||||||
|
{
|
||||||
|
bool messageReceived = false;
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
await catalogViewModel.InitializeAsync(null);
|
||||||
|
catalogViewModel.Brand = catalogViewModel.Brands.FirstOrDefault();
|
||||||
|
catalogViewModel.Type = catalogViewModel.Types.FirstOrDefault();
|
||||||
|
|
||||||
|
Xamarin.Forms.MessagingCenter.Subscribe<CatalogViewModel>(this, MessageKeys.Filter, (sender) =>
|
||||||
|
{
|
||||||
|
messageReceived = true;
|
||||||
|
});
|
||||||
|
catalogViewModel.FilterCommand.Execute(null);
|
||||||
|
|
||||||
|
Assert.True(messageReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ClearFilterCommandResetsPropertiesTest()
|
||||||
|
{
|
||||||
|
var catalogService = new CatalogMockService();
|
||||||
|
var catalogViewModel = new CatalogViewModel(catalogService);
|
||||||
|
|
||||||
|
await catalogViewModel.InitializeAsync(null);
|
||||||
|
catalogViewModel.ClearFilterCommand.Execute(null);
|
||||||
|
|
||||||
|
Assert.Null(catalogViewModel.Brand);
|
||||||
|
Assert.Null(catalogViewModel.Type);
|
||||||
|
Assert.NotNull(catalogViewModel.Products);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
using Xunit;
|
||||||
|
using eShopOnContainers.Core.ViewModels;
|
||||||
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
|
using eShopOnContainers.Core.Models.Navigation;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests
|
||||||
|
{
|
||||||
|
public class MainViewModelTests
|
||||||
|
{
|
||||||
|
public MainViewModelTests()
|
||||||
|
{
|
||||||
|
ViewModelLocator.RegisterDependencies(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SettingsCommandIsNotNullWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var mainViewModel = new MainViewModel();
|
||||||
|
Assert.NotNull(mainViewModel.SettingsCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ViewModelInitializationSendsChangeTabMessageTest()
|
||||||
|
{
|
||||||
|
bool messageReceived = false;
|
||||||
|
var mainViewModel = new MainViewModel();
|
||||||
|
var tabParam = new TabParameter { TabIndex = 2 };
|
||||||
|
|
||||||
|
Xamarin.Forms.MessagingCenter.Subscribe<MainViewModel, int>(this, MessageKeys.ChangeTab, (sender, arg) =>
|
||||||
|
{
|
||||||
|
messageReceived = true;
|
||||||
|
});
|
||||||
|
await mainViewModel.InitializeAsync(tabParam);
|
||||||
|
|
||||||
|
Assert.True(messageReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsBusyPropertyIsFalseWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var mainViewModel = new MainViewModel();
|
||||||
|
Assert.False(mainViewModel.IsBusy);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IsBusyPropertyIsTrueAfterViewModelInitializationTest()
|
||||||
|
{
|
||||||
|
var mainViewModel = new MainViewModel();
|
||||||
|
await mainViewModel.InitializeAsync(null);
|
||||||
|
Assert.True(mainViewModel.IsBusy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
using Xunit;
|
||||||
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests
|
||||||
|
{
|
||||||
|
public class MockViewModelTests
|
||||||
|
{
|
||||||
|
public MockViewModelTests()
|
||||||
|
{
|
||||||
|
ViewModelLocator.RegisterDependencies(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CheckValidationFailsWhenPropertiesAreEmptyTest()
|
||||||
|
{
|
||||||
|
var mockViewModel = new MockViewModel();
|
||||||
|
|
||||||
|
bool isValid = mockViewModel.Validate();
|
||||||
|
|
||||||
|
Assert.False(isValid);
|
||||||
|
Assert.Null(mockViewModel.Forename.Value);
|
||||||
|
Assert.Null(mockViewModel.Surname.Value);
|
||||||
|
Assert.False(mockViewModel.Forename.IsValid);
|
||||||
|
Assert.False(mockViewModel.Surname.IsValid);
|
||||||
|
Assert.NotEmpty(mockViewModel.Forename.Errors);
|
||||||
|
Assert.NotEmpty(mockViewModel.Surname.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CheckValidationFailsWhenOnlyForenameHasDataTest()
|
||||||
|
{
|
||||||
|
var mockViewModel = new MockViewModel();
|
||||||
|
mockViewModel.Forename.Value = "John";
|
||||||
|
|
||||||
|
bool isValid = mockViewModel.Validate();
|
||||||
|
|
||||||
|
Assert.False(isValid);
|
||||||
|
Assert.NotNull(mockViewModel.Forename.Value);
|
||||||
|
Assert.Null(mockViewModel.Surname.Value);
|
||||||
|
Assert.True(mockViewModel.Forename.IsValid);
|
||||||
|
Assert.False(mockViewModel.Surname.IsValid);
|
||||||
|
Assert.Empty(mockViewModel.Forename.Errors);
|
||||||
|
Assert.NotEmpty(mockViewModel.Surname.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CheckValidationPassesWhenOnlySurnameHasDataTest()
|
||||||
|
{
|
||||||
|
var mockViewModel = new MockViewModel();
|
||||||
|
mockViewModel.Surname.Value = "Smith";
|
||||||
|
|
||||||
|
bool isValid = mockViewModel.Validate();
|
||||||
|
|
||||||
|
Assert.False(isValid);
|
||||||
|
Assert.Null(mockViewModel.Forename.Value);
|
||||||
|
Assert.NotNull(mockViewModel.Surname.Value);
|
||||||
|
Assert.False(mockViewModel.Forename.IsValid);
|
||||||
|
Assert.True(mockViewModel.Surname.IsValid);
|
||||||
|
Assert.NotEmpty(mockViewModel.Forename.Errors);
|
||||||
|
Assert.Empty(mockViewModel.Surname.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CheckValidationPassesWhenBothPropertiesHaveDataTest()
|
||||||
|
{
|
||||||
|
var mockViewModel = new MockViewModel();
|
||||||
|
mockViewModel.Forename.Value = "John";
|
||||||
|
mockViewModel.Surname.Value = "Smith";
|
||||||
|
|
||||||
|
bool isValid = mockViewModel.Validate();
|
||||||
|
|
||||||
|
Assert.True(isValid);
|
||||||
|
Assert.NotNull(mockViewModel.Forename.Value);
|
||||||
|
Assert.NotNull(mockViewModel.Surname.Value);
|
||||||
|
Assert.True(mockViewModel.Forename.IsValid);
|
||||||
|
Assert.True(mockViewModel.Surname.IsValid);
|
||||||
|
Assert.Empty(mockViewModel.Forename.Errors);
|
||||||
|
Assert.Empty(mockViewModel.Surname.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SettingForenamePropertyShouldRaisePropertyChanged()
|
||||||
|
{
|
||||||
|
bool invoked = false;
|
||||||
|
var mockViewModel = new MockViewModel();
|
||||||
|
|
||||||
|
mockViewModel.Forename.PropertyChanged += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName.Equals("Value"))
|
||||||
|
invoked = true;
|
||||||
|
};
|
||||||
|
mockViewModel.Forename.Value = "John";
|
||||||
|
|
||||||
|
Assert.True(invoked);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SettingSurnamePropertyShouldRaisePropertyChanged()
|
||||||
|
{
|
||||||
|
bool invoked = false;
|
||||||
|
var mockViewModel = new MockViewModel();
|
||||||
|
|
||||||
|
mockViewModel.Surname.PropertyChanged += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName.Equals("Value"))
|
||||||
|
invoked = true;
|
||||||
|
};
|
||||||
|
mockViewModel.Surname.Value = "Smith";
|
||||||
|
|
||||||
|
Assert.True(invoked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
using Xunit;
|
||||||
|
using eShopOnContainers.Core;
|
||||||
|
using eShopOnContainers.Core.ViewModels;
|
||||||
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
|
using eShopOnContainers.Core.Services.Order;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.UnitTests
|
||||||
|
{
|
||||||
|
public class OrderViewModelTests
|
||||||
|
{
|
||||||
|
public OrderViewModelTests()
|
||||||
|
{
|
||||||
|
ViewModelLocator.RegisterDependencies(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OrderPropertyIsNullWhenViewModelInstantiatedTest()
|
||||||
|
{
|
||||||
|
var orderService = new OrderMockService();
|
||||||
|
var orderViewModel = new OrderDetailViewModel(orderService);
|
||||||
|
Assert.Null(orderViewModel.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OrderPropertyIsNotNullAfterViewModelInitializationTest()
|
||||||
|
{
|
||||||
|
var orderService = new OrderMockService();
|
||||||
|
var orderViewModel = new OrderDetailViewModel(orderService);
|
||||||
|
|
||||||
|
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
|
||||||
|
await orderViewModel.InitializeAsync(order);
|
||||||
|
|
||||||
|
Assert.NotNull(orderViewModel.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SettingOrderPropertyShouldRaisePropertyChanged()
|
||||||
|
{
|
||||||
|
bool invoked = false;
|
||||||
|
var orderService = new OrderMockService();
|
||||||
|
var orderViewModel = new OrderDetailViewModel(orderService);
|
||||||
|
|
||||||
|
orderViewModel.PropertyChanged += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName.Equals("Order"))
|
||||||
|
invoked = true;
|
||||||
|
};
|
||||||
|
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
|
||||||
|
await orderViewModel.InitializeAsync(order);
|
||||||
|
|
||||||
|
Assert.True(invoked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -34,11 +34,17 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="BasketServiceTests.cs" />
|
|
||||||
<Compile Include="CatalogServiceTests.cs" />
|
|
||||||
<Compile Include="DummyTests.cs" />
|
|
||||||
<Compile Include="OrdersServiceTests.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Mocks\MockEventToCommandBehavior.cs" />
|
||||||
|
<Compile Include="Services\BasketServiceTests.cs" />
|
||||||
|
<Compile Include="Services\CatalogServiceTests.cs" />
|
||||||
|
<Compile Include="ViewModels\CatalogViewModelTests.cs" />
|
||||||
|
<Compile Include="ViewModels\MainViewModelTests.cs" />
|
||||||
|
<Compile Include="ViewModels\OrderViewModelTests.cs" />
|
||||||
|
<Compile Include="Services\OrdersServiceTests.cs" />
|
||||||
|
<Compile Include="Behaviors\EventToCommandBehaviorTests.cs" />
|
||||||
|
<Compile Include="Mocks\MockViewModel.cs" />
|
||||||
|
<Compile Include="ViewModels\MockViewModelTests.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
@ -57,6 +63,15 @@
|
|||||||
<HintPath>..\..\..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.execution.dotnet.dll</HintPath>
|
<HintPath>..\..\..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.execution.dotnet.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Forms.Core">
|
||||||
|
<HintPath>..\..\..\..\packages\Xamarin.Forms.2.3.4.231\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Forms.Platform">
|
||||||
|
<HintPath>..\..\..\..\packages\Xamarin.Forms.2.3.4.231\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Platform.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xamarin.Forms.Xaml">
|
||||||
|
<HintPath>..\..\..\..\packages\Xamarin.Forms.2.3.4.231\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.Xaml.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
@ -68,6 +83,12 @@
|
|||||||
<Name>eShopOnContainers.Core</Name>
|
<Name>eShopOnContainers.Core</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Mocks\" />
|
||||||
|
<Folder Include="Services\" />
|
||||||
|
<Folder Include="ViewModels\" />
|
||||||
|
<Folder Include="Behaviors\" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
@ -76,4 +97,5 @@
|
|||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
|
<Import Project="..\..\..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets" Condition="Exists('..\..\..\..\packages\Xamarin.Forms.2.3.4.231\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" />
|
||||||
</Project>
|
</Project>
|
@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="xunit" version="2.2.0-beta4-build3444" targetFramework="portable46-net451+win81" />
|
<package id="Xamarin.Forms" version="2.3.4.231" targetFramework="portable46-net451+win81" />
|
||||||
<package id="xunit.abstractions" version="2.0.1" targetFramework="portable46-net451+win81" />
|
<package id="xunit" version="2.2.0-beta4-build3444" targetFramework="portable-net451+win81" />
|
||||||
<package id="xunit.assert" version="2.2.0-beta4-build3444" targetFramework="portable46-net451+win81" />
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="portable-net451+win81" requireReinstallation="True" />
|
||||||
<package id="xunit.core" version="2.2.0-beta4-build3444" targetFramework="portable46-net451+win81" />
|
<package id="xunit.assert" version="2.2.0-beta4-build3444" targetFramework="portable-net451+win81" requireReinstallation="True" />
|
||||||
<package id="xunit.extensibility.core" version="2.2.0-beta4-build3444" targetFramework="portable46-net451+win81" />
|
<package id="xunit.core" version="2.2.0-beta4-build3444" targetFramework="portable-net451+win81" requireReinstallation="True" />
|
||||||
<package id="xunit.extensibility.execution" version="2.2.0-beta4-build3444" targetFramework="portable46-net451+win81" />
|
<package id="xunit.extensibility.core" version="2.2.0-beta4-build3444" targetFramework="portable-net451+win81" requireReinstallation="True" />
|
||||||
<package id="xunit.runner.console" version="2.2.0-beta4-build3444" targetFramework="portable46-net451+win81" developmentDependency="true" />
|
<package id="xunit.extensibility.execution" version="2.2.0-beta4-build3444" targetFramework="portable-net451+win81" requireReinstallation="True" />
|
||||||
|
<package id="xunit.runner.console" version="2.2.0-beta4-build3444" targetFramework="portable-net451+win81" developmentDependency="true" />
|
||||||
</packages>
|
</packages>
|
1
src/Mobile/eShopOnContainers/eShopOnContainers.Windows/project.json
Normal file → Executable file
1
src/Mobile/eShopOnContainers/eShopOnContainers.Windows/project.json
Normal file → Executable file
@ -5,7 +5,6 @@
|
|||||||
"Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0",
|
"Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0",
|
||||||
"Newtonsoft.Json": "9.0.1",
|
"Newtonsoft.Json": "9.0.1",
|
||||||
"SlideOverKit": "2.1.4",
|
"SlideOverKit": "2.1.4",
|
||||||
"Unity": "4.0.1",
|
|
||||||
"Xam.Plugins.Settings": "2.6.0.12-beta",
|
"Xam.Plugins.Settings": "2.6.0.12-beta",
|
||||||
"Xamarin.FFImageLoading": "2.2.6-pre-256",
|
"Xamarin.FFImageLoading": "2.2.6-pre-256",
|
||||||
"Xamarin.FFImageLoading.Forms": "2.2.6-pre-256",
|
"Xamarin.FFImageLoading.Forms": "2.2.6-pre-256",
|
||||||
|
@ -166,10 +166,6 @@
|
|||||||
<HintPath>..\..\..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
|
<HintPath>..\..\..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Microsoft.Practices.Unity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=6d32ff45e0ccc69f, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Unity.4.0.1\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ModernHttpClient, Version=2.4.2.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="ModernHttpClient, Version=2.4.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\..\..\packages\modernhttpclient.2.4.2\lib\Xamarin.iOS10\ModernHttpClient.dll</HintPath>
|
<HintPath>..\..\..\..\packages\modernhttpclient.2.4.2\lib\Xamarin.iOS10\ModernHttpClient.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="xamarinios10" />
|
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="xamarinios10" />
|
||||||
<package id="SlideOverKit" version="2.1.4" targetFramework="xamarinios10" />
|
<package id="SlideOverKit" version="2.1.4" targetFramework="xamarinios10" />
|
||||||
<package id="Splat" version="1.6.2" targetFramework="xamarinios10" />
|
<package id="Splat" version="1.6.2" targetFramework="xamarinios10" />
|
||||||
<package id="Unity" version="4.0.1" targetFramework="xamarinios10" />
|
|
||||||
<package id="WebP.Touch" version="1.0.2" targetFramework="xamarinios10" />
|
<package id="WebP.Touch" version="1.0.2" targetFramework="xamarinios10" />
|
||||||
<package id="Xam.Plugins.Settings" version="2.6.0.12-beta" targetFramework="xamarinios10" />
|
<package id="Xam.Plugins.Settings" version="2.6.0.12-beta" targetFramework="xamarinios10" />
|
||||||
<package id="Xamarin.FFImageLoading" version="2.2.6-pre-256" targetFramework="xamarinios10" />
|
<package id="Xamarin.FFImageLoading" version="2.2.6-pre-256" targetFramework="xamarinios10" />
|
||||||
|
@ -43,7 +43,6 @@
|
|||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.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.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
|
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ using Basket.API.IntegrationEvents.EventHandling;
|
|||||||
using Basket.API.IntegrationEvents.Events;
|
using Basket.API.IntegrationEvents.Events;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
|
||||||
using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server;
|
using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server;
|
||||||
@ -19,6 +20,7 @@ using StackExchange.Redis;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Basket.API
|
namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||||
{
|
{
|
||||||
@ -68,20 +70,18 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
services.AddSingleton<IRabbitMQPersisterConnection>(sp =>
|
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
|
||||||
{
|
{
|
||||||
var settings = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
|
var settings = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
|
||||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersisterConnection>>();
|
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
|
||||||
var factory = new ConnectionFactory()
|
var factory = new ConnectionFactory()
|
||||||
{
|
{
|
||||||
HostName = settings.EventBusConnection
|
HostName = settings.EventBusConnection
|
||||||
};
|
};
|
||||||
|
|
||||||
return new DefaultRabbitMQPersisterConnection(factory, logger);
|
return new DefaultRabbitMQPersistentConnection(factory, logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
|
||||||
|
|
||||||
services.AddSwaggerGen();
|
services.AddSwaggerGen();
|
||||||
|
|
||||||
services.ConfigureSwaggerGen(options =>
|
services.ConfigureSwaggerGen(options =>
|
||||||
@ -108,9 +108,16 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
|||||||
});
|
});
|
||||||
|
|
||||||
services.AddTransient<IBasketRepository, RedisBasketRepository>();
|
services.AddTransient<IBasketRepository, RedisBasketRepository>();
|
||||||
services.AddTransient<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>, ProductPriceChangedIntegrationEventHandler>();
|
RegisterServiceBus(services);
|
||||||
services.AddTransient<IIntegrationEventHandler<OrderStartedIntegrationEvent>, OrderStartedIntegrationEventHandler>();
|
}
|
||||||
|
|
||||||
|
private void RegisterServiceBus(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
||||||
|
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
|
||||||
|
|
||||||
|
services.AddTransient<ProductPriceChangedIntegrationEventHandler>();
|
||||||
|
services.AddTransient<OrderStartedIntegrationEventHandler>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,11 +162,13 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
|||||||
var orderStartedHandler = app.ApplicationServices
|
var orderStartedHandler = app.ApplicationServices
|
||||||
.GetService<IIntegrationEventHandler<OrderStartedIntegrationEvent>>();
|
.GetService<IIntegrationEventHandler<OrderStartedIntegrationEvent>>();
|
||||||
|
|
||||||
var eventBus = app.ApplicationServices
|
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
|
||||||
.GetRequiredService<IEventBus>();
|
|
||||||
|
|
||||||
eventBus.Subscribe<ProductPriceChangedIntegrationEvent>(catalogPriceHandler);
|
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>
|
||||||
eventBus.Subscribe<OrderStartedIntegrationEvent>(orderStartedHandler);
|
(() => app.ApplicationServices.GetRequiredService<ProductPriceChangedIntegrationEventHandler>());
|
||||||
|
|
||||||
|
eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>
|
||||||
|
(() => app.ApplicationServices.GetRequiredService<OrderStartedIntegrationEventHandler>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.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.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
|
||||||
@ -103,18 +104,19 @@
|
|||||||
|
|
||||||
services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>();
|
services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>();
|
||||||
|
|
||||||
services.AddSingleton<IRabbitMQPersisterConnection>(sp =>
|
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
|
||||||
{
|
{
|
||||||
var settings = sp.GetRequiredService<IOptions<CatalogSettings>>().Value;
|
var settings = sp.GetRequiredService<IOptions<CatalogSettings>>().Value;
|
||||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersisterConnection>>();
|
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
|
||||||
var factory = new ConnectionFactory()
|
var factory = new ConnectionFactory()
|
||||||
{
|
{
|
||||||
HostName = settings.EventBusConnection
|
HostName = settings.EventBusConnection
|
||||||
};
|
};
|
||||||
|
|
||||||
return new DefaultRabbitMQPersisterConnection(factory, logger);
|
return new DefaultRabbitMQPersistentConnection(factory, logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
|
||||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -61,20 +61,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
|||||||
[DataMember]
|
[DataMember]
|
||||||
public IEnumerable<OrderItemDTO> OrderItems => _orderItems;
|
public IEnumerable<OrderItemDTO> OrderItems => _orderItems;
|
||||||
|
|
||||||
public void AddOrderItem(OrderItemDTO item)
|
|
||||||
{
|
|
||||||
_orderItems.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CreateOrderCommand()
|
public CreateOrderCommand()
|
||||||
{
|
{
|
||||||
_orderItems = new List<OrderItemDTO>();
|
_orderItems = new List<OrderItemDTO>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateOrderCommand(string city, string street, string state, string country, string zipcode,
|
public CreateOrderCommand(List<OrderItemDTO> orderItems, string city, string street, string state, string country, string zipcode,
|
||||||
string cardNumber, string cardHolderName, DateTime cardExpiration,
|
string cardNumber, string cardHolderName, DateTime cardExpiration,
|
||||||
string cardSecurityNumber, int cardTypeId, int paymentId, int buyerId) : this()
|
string cardSecurityNumber, int cardTypeId, int paymentId, int buyerId) : this()
|
||||||
{
|
{
|
||||||
|
_orderItems = orderItems;
|
||||||
City = city;
|
City = city;
|
||||||
Street = street;
|
Street = street;
|
||||||
State = state;
|
State = state;
|
||||||
|
@ -41,12 +41,12 @@
|
|||||||
{
|
{
|
||||||
var json = new JsonErrorResponse
|
var json = new JsonErrorResponse
|
||||||
{
|
{
|
||||||
Messages = new[] { "An error ocurr.Try it again." }
|
Messages = new[] { "An error occur.Try it again." }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
{
|
{
|
||||||
json.DeveloperMeesage = context.Exception;
|
json.DeveloperMessage = context.Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1
|
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1
|
||||||
@ -61,7 +61,7 @@
|
|||||||
{
|
{
|
||||||
public string[] Messages { get; set; }
|
public string[] Messages { get; set; }
|
||||||
|
|
||||||
public object DeveloperMeesage { get; set; }
|
public object DeveloperMessage { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" />
|
||||||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\Ordering.Domain\Ordering.Domain.csproj" />
|
<ProjectReference Include="..\Ordering.Domain\Ordering.Domain.csproj" />
|
||||||
<ProjectReference Include="..\Ordering.Infrastructure\Ordering.Infrastructure.csproj" />
|
<ProjectReference Include="..\Ordering.Infrastructure\Ordering.Infrastructure.csproj" />
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
|
||||||
@ -107,18 +108,19 @@
|
|||||||
var serviceProvider = services.BuildServiceProvider();
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
|
services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
|
||||||
|
|
||||||
services.AddSingleton<IRabbitMQPersisterConnection>(sp =>
|
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
|
||||||
{
|
{
|
||||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersisterConnection>>();
|
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
|
||||||
|
|
||||||
var factory = new ConnectionFactory()
|
var factory = new ConnectionFactory()
|
||||||
{
|
{
|
||||||
HostName = Configuration["EventBusConnection"]
|
HostName = Configuration["EventBusConnection"]
|
||||||
};
|
};
|
||||||
|
|
||||||
return new DefaultRabbitMQPersisterConnection(factory, logger);
|
return new DefaultRabbitMQPersistentConnection(factory, logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
|
||||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
|
||||||
|
|
||||||
services.AddOptions();
|
services.AddOptions();
|
||||||
|
@ -252,7 +252,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
|
|||||||
|
|
||||||
|
|
||||||
// After executing this line all the changes (from the Command Handler and Domain Event Handlers)
|
// After executing this line all the changes (from the Command Handler and Domain Event Handlers)
|
||||||
// performed thought the DbContext will be commited
|
// performed throught the DbContext will be commited
|
||||||
var result = await base.SaveChangesAsync();
|
var result = await base.SaveChangesAsync();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -59,7 +59,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
|
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
|
<ProjectReference Include="..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -74,7 +74,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
|
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
|
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user