Last active
April 18, 2019 16:57
-
-
Save jacobduijzer/9d2fc59a0b30a96c451f9609292d27dd to your computer and use it in GitHub Desktop.
Polly samples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class AuthenticationService : IAuthenticationService | |
| { | |
| private static readonly int NUMBER_OF_RETRIES = 3; | |
| private readonly IAuthenticationApi _authenticationApi; | |
| private readonly string _clientId; | |
| private readonly string _clientSecret; | |
| public AuthenticationService( | |
| IAuthenticationApi authenticationApi, | |
| string clientId, | |
| string clientSecret | |
| ) | |
| { | |
| Guard.Against.Null(authenticationApi, nameof(authenticationApi)); | |
| Guard.Against.NullOrEmpty(clientId, nameof(clientId)); | |
| Guard.Against.NullOrEmpty(clientSecret, nameof(clientSecret)); | |
| _authenticationApi = authenticationApi; | |
| _clientId = clientId; | |
| _clientSecret = clientSecret; | |
| } | |
| public async Task<AuthenticationResult> GetAccessToken() => | |
| await Policy | |
| .Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout) | |
| .RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) => await Task.Delay(500)) | |
| .ExecuteAsync(async () => await _authenticationApi.GetAccessToken(_clientId, _clientSecret).ConfigureAwait(false)) | |
| .ConfigureAwait(false); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Fact] | |
| public async Task RetryWhenReceivingTimeout() | |
| { | |
| // ARRANGE | |
| var mockApi = new Mock<IAuthenticationApi>(MockBehavior.Strict); | |
| mockApi.SetupSequence(x => x.GetAccessToken(CORRECT_CLIENT_ID, CORRECT_CLIENT_SECRET)) | |
| .Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout)) | |
| .Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout)) | |
| .ReturnsAsync(new AuthenticationResult { access_token = TEST_ACCESS_TOKEN }); | |
| var service = new AuthenticationService(mockApi.Object, CORRECT_CLIENT_ID, CORRECT_CLIENT_SECRET); | |
| // ACT | |
| var authResult = await service.GetAccessToken().ConfigureAwait(false); | |
| // ASSERT | |
| authResult.Should().NotBeNull(); | |
| authResult.access_token.Should().NotBeNullOrEmpty().And.Be(TEST_ACCESS_TOKEN); | |
| // The API Should have called GetAccessToken exactly 3 times: 2 times it received an exception, | |
| // the third time a correct result | |
| mockApi.Verify(x => x.GetAccessToken(It.IsAny<string>(), It.IsAny<string>()), Times.Exactly(3)); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public OrderService(IOrderApi orderApi, IQueueService queueService) | |
| { | |
| _orderApi = orderApi; | |
| _queueService = queueService; | |
| _circuitBreaker = Policy | |
| .Handle<Exception>() | |
| .CircuitBreakerAsync(EXCEPTIONS_ALLOWED_BEFORE_BREAKING_CIRCUIT,TimeSpan.FromMilliseconds(5000)); | |
| } | |
| public async Task SaveOrder(Order order) | |
| { | |
| var retryPolicy = Policy | |
| .Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout) | |
| .RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) => await Task.Delay(500) | |
| .ConfigureAwait(false)); | |
| var fallbackPolicy = Policy | |
| .Handle<Exception>() | |
| .FallbackAsync(async (cancellationToken) => await SaveOrderInQueue(order) | |
| .ConfigureAwait(false)); | |
| await fallbackPolicy | |
| .WrapAsync(retryPolicy) | |
| .WrapAsync(_circuitBreaker) | |
| .ExecuteAsync(async () => await _orderApi.SaveOrder(order).ConfigureAwait(false)) | |
| .ConfigureAwait(false); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Fact] | |
| public async Task BreakAfter10Tries() | |
| { | |
| var mockOrderApi = new Mock<IOrderApi>(MockBehavior.Strict); | |
| mockOrderApi.Setup(x => x.SaveOrder(It.IsAny<Order>())) | |
| .Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout)); | |
| var orderService = new OrderService(mockOrderApi.Object, _mockQueueService.Object, _testLogger); | |
| for (int i = 0; i <= 10; i++) | |
| await orderService.SaveOrder(TestHelper.CreateFakeOrder(3)).ConfigureAwait(false); | |
| mockOrderApi.Verify(x => x.SaveOrder(It.IsAny<Order>()), Times.AtLeast(10)); | |
| _mockQueueService.Verify(x => x.SaveOrder(It.IsAny<Order>()), Times.AtLeast(10)); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public async Task<List<Product>> GetProducts() | |
| { | |
| if (_authenticationResult == null) | |
| _authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false); | |
| var unauthorizedPolicy = Policy | |
| .Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.Unauthorized) | |
| .RetryAsync(async (exception, retryCount) => | |
| { | |
| _authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false); | |
| }); | |
| var timeoutPolicy = Policy | |
| .Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout) | |
| .RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) => | |
| await Task.Delay(300).ConfigureAwait(false)); | |
| return await unauthorizedPolicy | |
| .WrapAsync(timeoutPolicy) | |
| .ExecuteAsync(async () => await _productsApi.GetProductsAsync(_authenticationResult.access_token)) | |
| .ConfigureAwait(false); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public async Task SaveOrder(Order order) | |
| { | |
| var retryPolicy = Policy | |
| .Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout) | |
| .RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) => await Task.Delay(500).ConfigureAwait(false)); | |
| var fallbackPolicy = Policy | |
| .Handle<Exception>() | |
| .FallbackAsync(async (cancellationToken) => await SaveOrderInQueue(order).ConfigureAwait(false)); | |
| await fallbackPolicy | |
| .WrapAsync(retryPolicy) | |
| .ExecuteAsync(async () => await _orderApi.SaveOrder(order).ConfigureAwait(false)) | |
| .ConfigureAwait(false); | |
| } | |
| private async Task SaveOrderInQueue(Order order) => | |
| await _queueService.SaveOrder(order) | |
| .ConfigureAwait(false); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Fact] | |
| public async Task HandleFallbackOnMultipleTimeouts() | |
| { | |
| var mockOrderApi = new Mock<IOrderApi>(MockBehavior.Strict); | |
| mockOrderApi | |
| .SetupSequence(x => x.SaveOrder(It.IsAny<Order>())) | |
| .Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout)) | |
| .Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout)) | |
| .Throws(TestHelper.CreateRefitException(HttpMethod.Post, HttpStatusCode.RequestTimeout)); | |
| var orderService = new OrderService(mockOrderApi.Object, _mockQueueService.Object); | |
| await orderService.SaveOrder(TestHelper.CreateFakeOrder(3)).ConfigureAwait(false); | |
| mockOrderApi.Verify(x => x.SaveOrder(It.IsAny<Order>()), Times.Exactly(4)); | |
| _mockQueueService.Verify(x => x.SaveOrder(It.IsAny<Order>()), Times.Once); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| return await Policy | |
| // When recieving a ApiException with status code 408 | |
| .Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout) | |
| // Retry NUMBER_OF_TIMES but execute some code before retrying | |
| .RetryAsync(NUMBER_OF_RETRIES, async (exception, retryCount) => | |
| { | |
| // wait a while | |
| await Task.Delay(300).ConfigureAwait(false); | |
| }) | |
| // execute the command | |
| .ExecuteAsync(async () => await _productsApi.GetProductsAsync(_authenticationResult.access_token)) | |
| .ConfigureAwait(false); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class ProductService : IProductService | |
| { | |
| private readonly IProductsApi _productsApi; | |
| public ProductService(IProductsApi productsApi) => _productsApi = productsApi; | |
| public async Task<Product> GetProducts() => | |
| await _productsApi | |
| .GetProducts() | |
| .ConfigureAwait(false); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public async Task<List<Product>> GetProducts() | |
| { | |
| int tryCount = 0; | |
| while(tryCount < 3) | |
| { | |
| try | |
| { | |
| tryCount++; | |
| return await GetProductsAsync().ConfigureAwait(false); | |
| } | |
| catch (System.Exception) | |
| { | |
| await Task.Delay(500); | |
| } | |
| } | |
| throw new Exception("Something went wrong"); | |
| } | |
| private async Task<List<Product>> GetProductsAsync() => | |
| await _productsApi | |
| .GetProductsAsync() | |
| .ConfigureAwait(false); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public async Task<List<Product>> GetProducts() | |
| { | |
| if(_authenticationResult == null) | |
| _authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false); | |
| int tryCount = 0; | |
| while(tryCount < 3) | |
| { | |
| try | |
| { | |
| tryCount++; | |
| return await GetProductsAsync(_authenticationResult.access_token).ConfigureAwait(false); | |
| } | |
| catch (ApiException ex) | |
| { | |
| switch(ex.StatusCode) | |
| { | |
| case System.Net.HttpStatusCode.RequestTimeout: | |
| await Task.Delay(500); | |
| break; | |
| case System.Net.HttpStatusCode.Unauthorized: | |
| _authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false); | |
| break; | |
| } | |
| } | |
| catch(Exception ex) | |
| { | |
| } | |
| } | |
| throw new Exception("Something went wrong"); | |
| } | |
| private async Task<List<Product>> GetProductsAsync(string accessToken) => | |
| await _productsApi | |
| .GetProductsAsync(accessToken) | |
| .ConfigureAwait(false); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class ProductService : IProductService | |
| { | |
| private readonly IProductsApi _productsApi; | |
| private readonly IAuthenticationService _authenticationService; | |
| private AuthenticationResult _authenticationResult; | |
| public ProductService(IProductsApi productsApi, IAuthenticationService authenticationService) | |
| { | |
| _productsApi = productsApi; | |
| _authenticationService = authenticationService; | |
| } | |
| private static readonly int NUMBER_OF_RETRIES = 3; | |
| public async Task<List<Product>> GetProducts() | |
| { | |
| if (_authenticationResult == null) | |
| _authenticationResult = await _authenticationService.GetAccessToken().ConfigureAwait(false); | |
| return await Policy | |
| .Handle<Exception>() | |
| .RetryAsync(NUMBER_OF_RETRIES) | |
| .ExecuteAsync(async () => await _productsApi.GetProductsAsync(_authenticationResult.access_token)) | |
| .ConfigureAwait(false); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment