Merge from Dev
This commit is contained in:
commit
6803781bf8
@ -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:
|
||||
|
Binary file not shown.
@ -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
Normal file
BIN
img/ebook_xamarin_patterns_cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
@ -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>
|
@ -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
|
||||
{
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
|
||||
{
|
||||
public class ResiliencePolicy
|
||||
{
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
private Policy CreateRetryPolicy() =>
|
||||
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(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");
|
||||
}
|
||||
);
|
||||
|
@ -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
|
||||
{
|
@ -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}">
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
@ -24,5 +24,6 @@
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<Capability Name="privateNetworkClientServer" />
|
||||
</Capabilities>
|
||||
</Package>
|
@ -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.
|
||||
|
@ -49,7 +49,7 @@
|
||||
|
||||
services.AddHealthChecks(checks =>
|
||||
{
|
||||
checks.AddUrlCheck(Configuration["ExternalCatalogBaseUrl"]);
|
||||
checks.AddSqlCheck("CatalogDb", Configuration["ConnectionString"]);
|
||||
});
|
||||
|
||||
services.AddMvc(options =>
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
services.AddHealthChecks(checks =>
|
||||
{
|
||||
checks.AddSqlCheck("Ordering_Db", Configuration["ConnectionString"]);
|
||||
checks.AddSqlCheck("OrderingDb", Configuration["ConnectionString"]);
|
||||
});
|
||||
|
||||
services.AddEntityFrameworkSqlServer()
|
||||
|
@ -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; }
|
||||
|
@ -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,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,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
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -4,7 +4,7 @@
|
||||
"BasketUrl": "http://localhost:5103",
|
||||
"IdentityUrl": "http://localhost:5105",
|
||||
"CallBackUrl": "http://localhost:5100/",
|
||||
"ActivateCircuitBreaker": "True",
|
||||
"UseResilientHttp": "True",
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
src/Web/WebStatus/Viewmodels/NamedCheckResult.cs
Normal file
20
src/Web/WebStatus/Viewmodels/NamedCheckResult.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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…
x
Reference in New Issue
Block a user