Skip to content

Commit

Permalink
feat: added retry logic to HttpRemoteQueryRunner
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianKuesters committed Jul 25, 2024
1 parent a9216c2 commit 433a33d
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Net;
using System.Text.Json;
using FluentAssertions;
using Moq;
using RestSharp;
using Wemogy.CQRS.Common.ValueObjects;
using Wemogy.CQRS.Extensions.FastEndpoints.Common;
using Wemogy.CQRS.Extensions.FastEndpoints.RemoteQueryRunners;
using Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.TestApplication.Queries.RequestTestContext;
using Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.TestApplication.ValueObjects;

namespace Wemogy.CQRS.Extensions.FastEndpoints.UnitTests.HttpRemoteQueryRunners;

public class HttpRemoteQueryRunnerTests
{
[Fact]
public async Task QueryAsync_ShouldRetryRequestAndReturnResult()
{
// Arrange
var query = new RequestTestContextQuery();
var testContext = new TestContext()
{
UserId = Guid.NewGuid().ToString()
};
var resultContent = JsonSerializer.Serialize(testContext, JsonOptions.JsonSerializerOptions);
var responses = new Queue<RestResponse>();
responses.Enqueue(new RestResponse()
{
StatusCode = HttpStatusCode.ServiceUnavailable
});
responses.Enqueue(new RestResponse()
{
StatusCode = HttpStatusCode.OK,
Content = resultContent,

// Set IsSuccessful to true
IsSuccessStatusCode = true,
ResponseStatus = ResponseStatus.Completed
});

var restClientMock = new Mock<IRestClient>();
restClientMock
.Setup(
m => m.ExecuteAsync(
It.IsAny<RestRequest>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(responses.Dequeue);

var httpRemoteQueryRunner = new HttpRemoteQueryRunner<RequestTestContextQuery, TestContext>(restClientMock.Object, string.Empty);
var request = new QueryRequest<RequestTestContextQuery>(query, new List<CommandQueryDependency>());

// Act
var result = await httpRemoteQueryRunner.QueryAsync(request, CancellationToken.None);

// Assert
result.Should().BeEquivalentTo(testContext);
responses.Should().BeEmpty();
}

[Fact]
public async Task QueryAsync_ShouldThrowWithoutResultAfterMaxRetryReachedRequest()
{
// Arrange
var query = new RequestTestContextQuery();
var testContext = new TestContext()
{
UserId = Guid.NewGuid().ToString()
};
var restClientMock = new Mock<IRestClient>();
restClientMock
.Setup(
m => m.ExecuteAsync(
It.IsAny<RestRequest>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(() => new RestResponse()
{
StatusCode = HttpStatusCode.ServiceUnavailable
});

var httpRemoteQueryRunner = new HttpRemoteQueryRunner<RequestTestContextQuery, TestContext>(restClientMock.Object, string.Empty);
var request = new QueryRequest<RequestTestContextQuery>(query, new List<CommandQueryDependency>());

// Act
var exception = await Record.ExceptionAsync(() => httpRemoteQueryRunner.QueryAsync(request, CancellationToken.None));

// Assert
exception.Should().NotBeNull();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Text.Json;
using Polly;
using Polly.Contrib.WaitAndRetry;
using RestSharp;
using Wemogy.Core.Errors;
using Wemogy.Core.Extensions;
Expand All @@ -14,25 +16,35 @@ namespace Wemogy.CQRS.Extensions.FastEndpoints.RemoteQueryRunners;
public class HttpRemoteQueryRunner<TQuery, TResult> : IRemoteQueryRunner<TQuery, TResult>
where TQuery : IQuery<TResult>
{
private readonly RestClient _restClient;
private readonly IRestClient _restClient;

/// <summary>
/// This is the sub-path of the client base path
/// </summary>
private readonly string _urlPath;
private readonly IAsyncPolicy<RestResponse> _retryPolicy;

public HttpRemoteQueryRunner(RestClient restClient, string urlPath)
public HttpRemoteQueryRunner(IRestClient restClient, string urlPath)
{
_restClient = restClient;
_urlPath = urlPath;
var retryCount = 3;
var delay = Backoff.ExponentialBackoff(
TimeSpan.FromMilliseconds(100),
retryCount);
_retryPolicy = Policy
.HandleResult<RestResponse>(x => !x.IsSuccessful)
.WaitAndRetryAsync(delay);
}

public async Task<TResult> QueryAsync(QueryRequest<TQuery> query, CancellationToken cancellationToken)
{
var request = new RestRequest(_urlPath)
.AddJsonBody(query);

var response = await _restClient.PostAsync(request, cancellationToken: cancellationToken);
var response = await _retryPolicy.ExecuteAsync(
ct => _restClient.PostAsync(request, cancellationToken: ct),
cancellationToken);

if (!response.IsSuccessful)
{
Expand Down

0 comments on commit 433a33d

Please sign in to comment.