Browse Source

Merge from Dev

pull/150/head
Ramón Tomás 7 years ago
parent
commit
6803781bf8
45 changed files with 621 additions and 68 deletions
  1. +1
    -1
      docker-compose.override.yml
  2. BIN
      docs/Developing-Enterprise-Mobile-Applications-with-XamarinForms.pdf
  3. +54
    -0
      eShopOnContainers-ServicesAndWebApps.sln
  4. BIN
      img/ebook_xamarin_patterns_cover.png
  5. +14
    -0
      src/BuildingBlocks/Resilience/HttpResilience/HttpResilience.csproj
  6. +1
    -1
      src/BuildingBlocks/Resilience/HttpResilience/IHttpClient.cs
  7. +18
    -0
      src/BuildingBlocks/Resilience/HttpResilience/Policies/CircuitBreakerPolicy.cs
  8. +20
    -0
      src/BuildingBlocks/Resilience/HttpResilience/Policies/RetryPolicy.cs
  9. +10
    -0
      src/BuildingBlocks/Resilience/HttpResilience/ResiliencePolicy.cs
  10. +20
    -0
      src/BuildingBlocks/Resilience/HttpResilience/ResiliencePolicyFactory.cs
  11. +55
    -23
      src/BuildingBlocks/Resilience/HttpResilience/ResilientHttpClient.cs
  12. +1
    -1
      src/BuildingBlocks/Resilience/HttpResilience/StandardHttpClient.cs
  13. +3
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml
  14. +23
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Converters/WebNavigatedEventArgsConverter.cs
  15. +1
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs
  16. +10
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Basket/BasketItem.cs
  17. +3
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Orders/Order.cs
  18. +2
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/BasketViewModel.cs
  19. +3
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CheckoutViewModel.cs
  20. +4
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs
  21. +20
    -4
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml
  22. +11
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml.cs
  23. +16
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/Templates/BasketItemTemplate.xaml
  24. +1
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj
  25. +1
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Package.appxmanifest
  26. +1
    -1
      src/Services/Basket/Basket.API/Startup.cs
  27. +1
    -1
      src/Services/Catalog/Catalog.API/Startup.cs
  28. +6
    -0
      src/Services/Identity/Identity.API/Certificate/Certificate.cs
  29. +244
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170403082405_NoBuyerPropertyInOrder.Designer.cs
  30. +19
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170403082405_NoBuyerPropertyInOrder.cs
  31. +1
    -1
      src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs
  32. +1
    -1
      src/Services/Ordering/Ordering.API/Startup.cs
  33. +0
    -1
      src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs
  34. +1
    -1
      src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs
  35. +1
    -1
      src/Web/WebMVC/Services/BasketService.cs
  36. +1
    -1
      src/Web/WebMVC/Services/CatalogService.cs
  37. +1
    -1
      src/Web/WebMVC/Services/OrderingService.cs
  38. +9
    -3
      src/Web/WebMVC/Startup.cs
  39. +1
    -1
      src/Web/WebMVC/WebMVC.csproj
  40. +1
    -1
      src/Web/WebMVC/appsettings.json
  41. +2
    -1
      src/Web/WebSPA/Startup.cs
  42. +1
    -1
      src/Web/WebStatus/Controllers/HomeController.cs
  43. +4
    -4
      src/Web/WebStatus/Viewmodels/HealthStatusViewModel.cs
  44. +20
    -0
      src/Web/WebStatus/Viewmodels/NamedCheckResult.cs
  45. +14
    -10
      src/Web/WebStatus/Views/Home/Index.cshtml

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

@ -85,7 +85,7 @@ services:
- CatalogUrl=http://catalog.api:5101/hc
- OrderingUrl=http://ordering.api:5102/hc
- BasketUrl=http://basket.api:5103/hc
- IdentityUrl=http://10.0.75.1:5105/hc
- IdentityUrl=http://identity.api:5105/hc
- mvc=http://webmvc:5100/hc
- spa=http://webspa:5104/hc
ports:


BIN
docs/Developing-Enterprise-Mobile-Applications-with-XamarinForms.pdf View File


+ 54
- 0
eShopOnContainers-ServicesAndWebApps.sln View File

@ -84,6 +84,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Health
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience", "{FBF43D93-F2E7-4FF8-B4AB-186895949B88}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpResilience", "src\BuildingBlocks\Resilience\HttpResilience\HttpResilience.csproj", "{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@ -914,6 +918,54 @@ Global
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|Any CPU.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|ARM.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|ARM.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhone.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhone.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -947,5 +999,7 @@ Global
{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}
{FBF43D93-F2E7-4FF8-B4AB-186895949B88} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502} = {FBF43D93-F2E7-4FF8-B4AB-186895949B88}
EndGlobalSection
EndGlobal

BIN
img/ebook_xamarin_patterns_cover.png View File

Before After
Width: 310  |  Height: 402  |  Size: 30 KiB

+ 14
- 0
src/BuildingBlocks/Resilience/HttpResilience/HttpResilience.csproj View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Polly" Version="5.0.6" />
</ItemGroup>
</Project>

src/Web/WebMVC/Services/Utilities/IHttpClient.cs → src/BuildingBlocks/Resilience/HttpResilience/IHttpClient.cs View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace WebMVC.Services.Utilities
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
{
public interface IHttpClient
{

+ 18
- 0
src/BuildingBlocks/Resilience/HttpResilience/Policies/CircuitBreakerPolicy.cs View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience.Policies
{
internal class CircuitBreakerPolicy : ResiliencePolicy
{
public CircuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes)
{
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
DurationOfBreakInMinutes = durationOfBreakInMinutes;
}
public int ExceptionsAllowedBeforeBreaking { get; }
public int DurationOfBreakInMinutes { get; }
}
}

+ 20
- 0
src/BuildingBlocks/Resilience/HttpResilience/Policies/RetryPolicy.cs View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience.Policies
{
internal class RetryPolicy : ResiliencePolicy
{
public RetryPolicy(int retries, int backoffSeconds, bool exponentialBackoff)
{
Retries = retries;
BackoffSeconds = backoffSeconds;
ExponentialBackoff = exponentialBackoff;
}
public int Retries { get; }
public int BackoffSeconds { get; }
public bool ExponentialBackoff { get; }
}
}

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

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

+ 20
- 0
src/BuildingBlocks/Resilience/HttpResilience/ResiliencePolicyFactory.cs View File

@ -0,0 +1,20 @@
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience.Policies;
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
{
public static class ResiliencePolicyFactory
{
public static ResiliencePolicy CreateRetryPolicy(int retries, int backoffSeconds, bool exponentialBackoff)
{
return new RetryPolicy(retries, backoffSeconds, exponentialBackoff);
}
public static ResiliencePolicy CreateCiscuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes)
{
return new CircuitBreakerPolicy(exceptionsAllowedBeforeBreaking, durationOfBreakInMinutes);
}
}
}

src/Web/WebMVC/Services/Utilities/ResilientHttpClient.cs → src/BuildingBlocks/Resilience/HttpResilience/ResilientHttpClient.cs View File

@ -1,18 +1,20 @@
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience.Policies;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Polly;
using Polly.Wrap;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace WebMVC.Services.Utilities
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
{
/// <summary>
/// HttpClient wrapper that integrates Retry and Circuit
/// breaker policies when invoking HTTP services.
/// Based on Polly library: https://github.com/App-vNext/Polly
/// </summary>
public class ResilientHttpClient : IHttpClient
{
@ -20,26 +22,58 @@ namespace WebMVC.Services.Utilities
private PolicyWrap _policyWrapper;
private ILogger<ResilientHttpClient> _logger;
public HttpClient Inst => _client;
public ResilientHttpClient(ILogger<ResilientHttpClient> logger)
public ResilientHttpClient(List<Policy> policies, ILogger<ResilientHttpClient> logger)
{
_client = new HttpClient();
_logger = logger;
// Add Policies to be applied
_policyWrapper = Policy.WrapAsync(
CreateRetryPolicy(),
CreateCircuitBreakerPolicy()
);
_policyWrapper = Policy.WrapAsync(policies.ToArray());
}
public ResilientHttpClient(List<ResiliencePolicy> policies, ILogger<ResilientHttpClient> logger)
{
_client = new HttpClient();
_logger = logger;
// Add Policies to be applied
_policyWrapper = Policy.WrapAsync(GeneratePolicies(policies));
}
private Policy[] GeneratePolicies(IList<ResiliencePolicy> policies)
{
var pollyPolicies = new List<Policy>();
foreach (var policy in policies)
{
switch (policy)
{
case RetryPolicy retryPolicy:
pollyPolicies.Add(
CreateRetryPolicy(
retryPolicy.Retries,
retryPolicy.BackoffSeconds,
retryPolicy.ExponentialBackoff));
break;
case CircuitBreakerPolicy circuitPolicy:
pollyPolicies.Add(
CreateCircuitBreakerPolicy(
circuitPolicy.ExceptionsAllowedBeforeBreaking,
circuitPolicy.DurationOfBreakInMinutes));
break;
}
}
return pollyPolicies.ToArray();
}
private Policy CreateRetryPolicy() =>
private Policy CreateRetryPolicy(int retries, int backoffSeconds, bool exponentialBackoff) =>
Policy.Handle<HttpRequestException>()
.WaitAndRetryAsync(
// number of retries
6,
// exponential backofff
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
// on retry
.WaitAndRetryAsync(
retries,
retryAttempt => exponentialBackoff ? TimeSpan.FromSeconds(Math.Pow(backoffSeconds, retryAttempt)) : TimeSpan.FromSeconds(backoffSeconds),
(exception, timeSpan, retryCount, context) =>
{
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
@ -51,22 +85,20 @@ namespace WebMVC.Services.Utilities
}
);
private Policy CreateCircuitBreakerPolicy() =>
private Policy CreateCircuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes) =>
Policy.Handle<HttpRequestException>()
.CircuitBreakerAsync(
// number of exceptions before breaking circuit
5,
// time circuit opened before retry
TimeSpan.FromMinutes(1),
exceptionsAllowedBeforeBreaking,
TimeSpan.FromMinutes(durationOfBreakInMinutes),
(exception, duration) =>
{
// on circuit opened
_logger.LogTrace("Circuit breaker opened");
// on circuit opened
_logger.LogTrace("Circuit breaker opened");
},
() =>
{
// on circuit closed
_logger.LogTrace("Circuit breaker reset");
// on circuit closed
_logger.LogTrace("Circuit breaker reset");
}
);

src/Web/WebMVC/Services/Utilities/StandardHttpClient.cs → src/BuildingBlocks/Resilience/HttpResilience/StandardHttpClient.cs View File

@ -4,7 +4,7 @@ using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace WebMVC.Services.Utilities
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
{
public class StandardHttpClient : IHttpClient
{

+ 3
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml View File

@ -106,8 +106,9 @@
<converters:InverseBoolConverter x:Key="InverseBoolConverter" />
<converters:ItemsToHeightConverter x:Key="ItemsToHeightConverter" />
<converters:ToUpperConverter x:Key="ToUpperConverter" />
<converters:WebNavigatingEventArgsConverter x:Key="WebNavigatingEventArgsConverter" />
<converters:WebNavigatingEventArgsConverter x:Key="WebNavigatingEventArgsConverter" />
<converters:WebNavigatedEventArgsConverter x:Key="WebNavigatedEventArgsConverter" />
<!-- STYLES -->
<Style x:Key="EntryStyle"
TargetType="{x:Type Entry}">


+ 23
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Converters/WebNavigatedEventArgsConverter.cs View File

@ -0,0 +1,23 @@
using System;
using System.Globalization;
using Xamarin.Forms;
namespace eShopOnContainers.Core.Converters
{
public class WebNavigatedEventArgsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var eventArgs = value as WebNavigatedEventArgs;
if (eventArgs == null)
throw new ArgumentException("Expected WebNavigatedEventArgs as value", "value");
return eventArgs.Url;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

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

@ -60,7 +60,7 @@
IdentityEndpoint = string.Format("{0}:5105/connect/authorize", baseEndpoint);
UserInfoEndpoint = string.Format("{0}:5105/connect/userinfo", baseEndpoint);
LogoutEndpoint = string.Format("{0}:5105/connect/endsession", baseEndpoint);
IdentityCallback = "http://eshopxamarin/callback.html";
IdentityCallback = string.Format("{0}:5105/xamarincallback", baseEndpoint);
LogoutCallback = string.Format("{0}:5105/Account/Redirecting", baseEndpoint);
}
}

+ 10
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Basket/BasketItem.cs View File

@ -15,6 +15,16 @@ namespace eShopOnContainers.Core.Models.Basket
public decimal UnitPrice { get; set; }
public decimal OldUnitPrice { get; set; }
public bool HasNewPrice
{
get
{
return OldUnitPrice != 0.0m;
}
}
public int Quantity
{
get { return _quantity; }


+ 3
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Orders/Order.cs View File

@ -34,6 +34,9 @@ namespace eShopOnContainers.Core.Models.Orders
[JsonProperty("country")]
public string ShippingCountry { get; set; }
[JsonProperty("zipCode")]
public string ShippingZipCode { get; set; }
public int CardTypeId { get; set; }
public string CardNumber { get; set; }


+ 2
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/BasketViewModel.cs View File

@ -78,7 +78,9 @@ namespace eShopOnContainers.Core.ViewModels
if (basket != null && basket.Items != null && basket.Items.Any())
{
BadgeCount = 0;
BasketItems.Clear();
foreach (var basketItem in basket.Items)
{
BadgeCount += basketItem.Quantity;


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

@ -90,7 +90,7 @@ namespace eShopOnContainers.Core.ViewModels
ZipCode = userInfo?.ZipCode,
State = userInfo?.State,
Country = userInfo?.Country,
City = string.Empty
City = userInfo?.Address
};
// Create Payment Info
@ -117,7 +117,8 @@ namespace eShopOnContainers.Core.ViewModels
ShippingState = _shippingAddress.State,
ShippingCountry = _shippingAddress.Country,
ShippingStreet = _shippingAddress.Street,
ShippingCity = _shippingAddress.City,
ShippingCity = _shippingAddress.City,
ShippingZipCode = _shippingAddress.ZipCode,
Total = CalculateTotal(CreateOrderItems(orderItems))
};


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

@ -221,14 +221,16 @@ namespace eShopOnContainers.Core.ViewModels
private async Task NavigateAsync(string url)
{
if (url.Equals(GlobalSetting.Instance.LogoutCallback))
var unescapedUrl = System.Net.WebUtility.UrlDecode(url);
if (unescapedUrl.Equals(GlobalSetting.Instance.LogoutCallback))
{
Settings.AuthAccessToken = string.Empty;
Settings.AuthIdToken = string.Empty;
IsLogin = false;
LoginUrl = _identityService.CreateAuthorizeRequest();
}
else if (url.Contains(GlobalSetting.Instance.IdentityCallback))
else if (unescapedUrl.Contains(GlobalSetting.Instance.IdentityCallback))
{
var authResponse = new AuthorizeResponse(url);


+ 20
- 4
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml View File

@ -313,10 +313,26 @@
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All">
<WebView.Behaviors>
<behaviors:EventToCommandBehavior
EventName="Navigating"
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
Command="{Binding NavigateCommand}" />
<OnPlatform x:TypeArguments="Behavior">
<OnPlatform.Android>
<behaviors:EventToCommandBehavior
EventName="Navigating"
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
Command="{Binding NavigateCommand}" />
</OnPlatform.Android>
<OnPlatform.iOS>
<behaviors:EventToCommandBehavior
EventName="Navigating"
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
Command="{Binding NavigateCommand}" />
</OnPlatform.iOS>
<OnPlatform.WinPhone>
<behaviors:EventToCommandBehavior
EventName="Navigated"
EventArgsConverter="{StaticResource WebNavigatedEventArgsConverter}"
Command="{Binding NavigateCommand}" />
</OnPlatform.WinPhone>
</OnPlatform>
</WebView.Behaviors>
</WebView>
</AbsoluteLayout>


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

@ -44,5 +44,16 @@ namespace eShopOnContainers.Core.Views
await profileViewModel.InitializeAsync(null);
ProfileView.BindingContext = profileViewModel;
}
protected override async void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
if (CurrentPage is BasketView)
{
// Force basket view refresh every time we access it
await (BasketView.BindingContext as ViewModelBase).InitializeAsync(null);
}
}
}
}

+ 16
- 1
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/Templates/BasketItemTemplate.xaml View File

@ -83,6 +83,7 @@
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<!-- IMAGE -->
@ -131,10 +132,24 @@
Text="{Binding Total, StringFormat='${0:N}'}"
Style="{StaticResource OrderTotalStyle}"/>
</Grid>
<Grid
<Grid
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="1"
IsVisible="{Binding HasNewPrice}"
BackgroundColor="#F0AD4E">
<Label
HorizontalOptions="Fill"
VerticalOptions="Fill"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HeightRequest="60"
Text="{Binding OldUnitPrice, StringFormat='Note that the price of this article changed in our Catalog. The old price when you originally added it to the basket was ${0:N2}'}" />
</Grid>
<Grid
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="2"
BackgroundColor="Gray"/>
</Grid>
</ContentView.Content>

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

@ -58,6 +58,7 @@
<Compile Include="Converters\ItemsToHeightConverter.cs" />
<Compile Include="Converters\ItemTappedEventArgsConverter.cs" />
<Compile Include="Converters\ToUpperConverter.cs" />
<Compile Include="Converters\WebNavigatedEventArgsConverter.cs" />
<Compile Include="Effects\LineColorEffect.cs" />
<Compile Include="Exceptions\ServiceAuthenticationException.cs" />
<Compile Include="Extensions\AnimationExtension.cs" />


+ 1
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Package.appxmanifest View File

@ -24,5 +24,6 @@
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<Capability Name="privateNetworkClientServer" />
</Capabilities>
</Package>

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

@ -42,7 +42,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
services.AddHealthChecks(checks =>
{
checks.AddValueTaskCheck("Always OK", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Ok")));
checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Ok")));
});
// Add framework services.


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

@ -49,7 +49,7 @@
services.AddHealthChecks(checks =>
{
checks.AddUrlCheck(Configuration["ExternalCatalogBaseUrl"]);
checks.AddSqlCheck("CatalogDb", Configuration["ConnectionString"]);
});
services.AddMvc(options =>


+ 6
- 0
src/Services/Identity/Identity.API/Certificate/Certificate.cs View File

@ -13,6 +13,12 @@ namespace Identity.API.Certificate
{
var assembly = typeof(Certificate).GetTypeInfo().Assembly;
var names = assembly.GetManifestResourceNames();
/***********************************************************************************************
* Please note that here we are using a local certificate only for testing purposes. In a
* real environment the certificate should be created and stored in a secure way, which is out
* of the scope of this project.
**********************************************************************************************/
using (var stream = assembly.GetManifestResourceStream("Identity.API.Certificate.idsrv3test.pfx"))
{
return new X509Certificate2(ReadStream(stream), "idsrv3test");


+ 244
- 0
src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170403082405_NoBuyerPropertyInOrder.Designer.cs View File

@ -0,0 +1,244 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingContext))]
[Migration("20170403082405_NoBuyerPropertyInOrder")]
partial class NoBuyerPropertyInOrder
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752")
.HasAnnotation("SqlServer:Sequence:.orderitemseq", "'orderitemseq', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:ordering.buyerseq", "'buyerseq', 'ordering', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:ordering.orderseq", "'orderseq', 'ordering', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:ordering.paymentseq", "'paymentseq', 'ordering', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "buyerseq")
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("IdentityGuid")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.HasIndex("IdentityGuid")
.IsUnique();
b.ToTable("buyers","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.CardType", b =>
{
b.Property<int>("Id")
.HasDefaultValue(1);
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.ToTable("cardtypes","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "paymentseq")
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Alias")
.IsRequired()
.HasMaxLength(200);
b.Property<int>("BuyerId");
b.Property<string>("CardHolderName")
.IsRequired()
.HasMaxLength(200);
b.Property<string>("CardNumber")
.IsRequired()
.HasMaxLength(25);
b.Property<int>("CardTypeId");
b.Property<DateTime>("Expiration");
b.HasKey("Id");
b.HasIndex("BuyerId");
b.HasIndex("CardTypeId");
b.ToTable("paymentmethods","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("City");
b.Property<string>("Country");
b.Property<string>("State");
b.Property<string>("Street");
b.Property<string>("ZipCode");
b.HasKey("Id");
b.ToTable("address","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "orderseq")
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<int?>("AddressId");
b.Property<int?>("BuyerId");
b.Property<DateTime>("OrderDate");
b.Property<int>("OrderStatusId");
b.Property<int?>("PaymentMethodId");
b.HasKey("Id");
b.HasIndex("AddressId");
b.HasIndex("BuyerId");
b.HasIndex("OrderStatusId");
b.HasIndex("PaymentMethodId");
b.ToTable("orders","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "orderitemseq")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<decimal>("Discount");
b.Property<int>("OrderId");
b.Property<string>("PictureUrl");
b.Property<int>("ProductId");
b.Property<string>("ProductName")
.IsRequired();
b.Property<decimal>("UnitPrice");
b.Property<int>("Units");
b.HasKey("Id");
b.HasIndex("OrderId");
b.ToTable("orderItems","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", b =>
{
b.Property<int>("Id")
.HasDefaultValue(1);
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.ToTable("orderstatus","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.ClientRequest", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired();
b.Property<DateTime>("Time");
b.HasKey("Id");
b.ToTable("requests","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer")
.WithMany("PaymentMethods")
.HasForeignKey("BuyerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.CardType", "CardType")
.WithMany()
.HasForeignKey("CardTypeId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", "Address")
.WithMany()
.HasForeignKey("AddressId");
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer")
.WithMany()
.HasForeignKey("BuyerId");
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", "OrderStatus")
.WithMany()
.HasForeignKey("OrderStatusId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", "PaymentMethod")
.WithMany()
.HasForeignKey("PaymentMethodId");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order")
.WithMany("OrderItems")
.HasForeignKey("OrderId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 19
- 0
src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170403082405_NoBuyerPropertyInOrder.cs View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ordering.API.Migrations
{
public partial class NoBuyerPropertyInOrder : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

+ 1
- 1
src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs View File

@ -217,7 +217,7 @@ namespace Ordering.API.Migrations
.WithMany()
.HasForeignKey("AddressId");
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", "Buyer")
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer")
.WithMany()
.HasForeignKey("BuyerId");


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

@ -59,7 +59,7 @@
services.AddHealthChecks(checks =>
{
checks.AddSqlCheck("Ordering_Db", Configuration["ConnectionString"]);
checks.AddSqlCheck("OrderingDb", Configuration["ConnectionString"]);
});
services.AddEntityFrameworkSqlServer()


+ 0
- 1
src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs View File

@ -18,7 +18,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
public Address Address { get; private set; }
public Buyer Buyer { get; private set; }
private int? _buyerId;
public OrderStatus OrderStatus { get; private set; }


+ 1
- 1
src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs View File

@ -160,7 +160,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
orderConfiguration.HasOne(o => o.Buyer)
orderConfiguration.HasOne<Buyer>()
.WithMany()
.IsRequired(false)
.HasForeignKey("BuyerId");


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

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
@ -7,7 +8,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using WebMVC.Services.Utilities;
namespace Microsoft.eShopOnContainers.WebMVC.Services
{


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

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -6,7 +7,6 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using WebMVC.Services.Utilities;
namespace Microsoft.eShopOnContainers.WebMVC.Services
{


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

@ -8,7 +8,7 @@ using Microsoft.Extensions.Options;
using System.Net.Http;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Authentication;
using WebMVC.Services.Utilities;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience;
namespace Microsoft.eShopOnContainers.WebMVC.Services
{


+ 9
- 3
src/Web/WebMVC/Startup.cs View File

@ -14,8 +14,8 @@ using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http;
using System.Threading;
using Microsoft.Extensions.Options;
using WebMVC.Services.Utilities;
using Microsoft.Extensions.HealthChecks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience;
namespace Microsoft.eShopOnContainers.WebMVC
{
@ -48,7 +48,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
services.AddHealthChecks(checks =>
{
checks.AddUrlCheck(Configuration["CallBackUrl"]);
checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Ok")));
});
// Add application services.
@ -58,8 +58,14 @@ namespace Microsoft.eShopOnContainers.WebMVC
services.AddTransient<IBasketService, BasketService>();
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
if(Configuration.GetValue<string>("ActivateCircuitBreaker") == bool.TrueString)
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
{
services.AddSingleton(
new List<ResiliencePolicy>
{
ResiliencePolicyFactory.CreateRetryPolicy(6, 2, true),
ResiliencePolicyFactory.CreateCiscuitBreakerPolicy(5, 1)
});
services.AddTransient<IHttpClient, ResilientHttpClient>();
}
else


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

@ -32,7 +32,6 @@
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Polly" Version="5.0.6" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.0" />
@ -55,6 +54,7 @@
<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\Resilience\HttpResilience\HttpResilience.csproj" />
</ItemGroup>
<ItemGroup>


+ 1
- 1
src/Web/WebMVC/appsettings.json View File

@ -4,7 +4,7 @@
"BasketUrl": "http://localhost:5103",
"IdentityUrl": "http://localhost:5105",
"CallBackUrl": "http://localhost:5100/",
"ActivateCircuitBreaker": "True",
"UseResilientHttp": "True",
"Logging": {
"IncludeScopes": false,
"LogLevel": {


+ 2
- 1
src/Web/WebSPA/Startup.cs View File

@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Serialization;
using eShopOnContainers.WebSPA;
using Microsoft.Extensions.HealthChecks;
using System.Threading.Tasks;
namespace eShopConContainers.WebSPA
{
@ -42,7 +43,7 @@ namespace eShopConContainers.WebSPA
{
services.AddHealthChecks(checks =>
{
checks.AddUrlCheck(Configuration["CallBackUrl"]);
checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Ok")));
});
services.Configure<AppSettings>(Configuration);


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

@ -25,7 +25,7 @@ namespace WebStatus.Controllers
foreach (var checkResult in result.Results)
{
data.AddResult(checkResult.Value);
data.AddResult(checkResult.Key, checkResult.Value);
}
return View(data);


+ 4
- 4
src/Web/WebStatus/Viewmodels/HealthStatusViewModel.cs View File

@ -9,13 +9,13 @@ namespace WebStatus.Viewmodels
public class HealthStatusViewModel
{
private readonly CheckStatus _overall;
private readonly List<IHealthCheckResult> _results;
private readonly Dictionary<string, IHealthCheckResult> _results;
public CheckStatus OverallStatus => _overall;
public IEnumerable<IHealthCheckResult> Results => _results;
private HealthStatusViewModel() => _results = new List<IHealthCheckResult>();
public IEnumerable<NamedCheckResult> Results => _results.Select(kvp => new NamedCheckResult(kvp.Key, kvp.Value));
private HealthStatusViewModel() => _results = new Dictionary<string, IHealthCheckResult>();
public HealthStatusViewModel(CheckStatus overall) : this() => _overall = overall;
public void AddResult(IHealthCheckResult result) => _results.Add(result);
public void AddResult(string name, IHealthCheckResult result) => _results.Add(name, result);
}


+ 20
- 0
src/Web/WebStatus/Viewmodels/NamedCheckResult.cs View File

@ -0,0 +1,20 @@
using Microsoft.Extensions.HealthChecks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebStatus.Viewmodels
{
public class NamedCheckResult
{
public string Name { get; }
public IHealthCheckResult Result { get; }
public NamedCheckResult(string name, IHealthCheckResult result)
{
Name = name;
Result = result;
}
}
}

+ 14
- 10
src/Web/WebStatus/Views/Home/Index.cshtml View File

@ -16,26 +16,30 @@
{
<div class="row list-group-status-item">
<div class="col-md-10">
<h4 class="list-group-status-item-title">@result.Data["url"]</h4>
<p class="list-group-item-text">@result.Description</p>
<h4 class="list-group-status-item-title">@result.Name</h4>
<p class="list-group-item-text">
@if (result.Result.Data.ContainsKey("url")) {
<p>@result.Result.Data["url"]</p>
}
@result.Result.Description
</p>
</div>
<div class="col-md-2 list-group-status-item-label">
@if (@result.CheckStatus == Microsoft.Extensions.HealthChecks.CheckStatus.Healthy)
@if (@result.Result.CheckStatus == Microsoft.Extensions.HealthChecks.CheckStatus.Healthy)
{
<span class="label label-success">@result.CheckStatus</span>
<span class="label label-success">@result.Result.CheckStatus</span>
}
else if (@result.CheckStatus == Microsoft.Extensions.HealthChecks.CheckStatus.Unhealthy)
else if (@result.Result.CheckStatus == Microsoft.Extensions.HealthChecks.CheckStatus.Unhealthy)
{
<span class="label label-danger">@result.CheckStatus</span>
<span class="label label-danger">@result.Result.CheckStatus</span>
}
else if (@result.CheckStatus == Microsoft.Extensions.HealthChecks.CheckStatus.Warning)
else if (@result.Result.CheckStatus == Microsoft.Extensions.HealthChecks.CheckStatus.Warning)
{
<span class="label label-warning">@result.CheckStatus</span>
<span class="label label-warning">@result.Result.CheckStatus</span>
}
else
{
<span class="label label-default">@result.CheckStatus</span>
<span class="label label-default">@result.Result.CheckStatus</span>
}
</div>
</div>


Loading…
Cancel
Save