diff --git a/.github/workflows/publish_nuget_package.yml b/.github/workflows/publish_nuget_package.yml index f2a9d49..e13ae23 100644 --- a/.github/workflows/publish_nuget_package.yml +++ b/.github/workflows/publish_nuget_package.yml @@ -42,7 +42,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v5 with: - dotnet-version: '8.x' + dotnet-version: '10.x' - name: Clear NuGet Cache run: dotnet nuget locals all --clear diff --git a/.gitignore b/.gitignore index 41faeb1..a283660 100644 --- a/.gitignore +++ b/.gitignore @@ -103,11 +103,6 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover -# NCrunch -_NCrunch_* -.*crunch*.local.xml -*.v3.ncrunchsolution.user - # MightyMoose *.mm.* AutoTest.Net/ diff --git a/README.md b/README.md index 9d20050..3c2d85a 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,7 @@ For full API documentation, please visit [evidos.github.io](https://evidos.githu For detailed examples, visit: [https://github.com/Evidos/SignhostClientLibrary/blob/master/README.md](https://github.com/Evidos/SignhostClientLibrary/blob/master/README.md) ```c# -var settings = new SignHostApiClientSettings("YourAppKey", "apikey or usertoken"); - -var retryClient = new SignHostApiRetryClient(settings); +var settings = new SignhostApiClientSettings("YourAppKey", "apikey or usertoken"); +var retryClient = new SignhostApiRetryClient(settings); ``` diff --git a/src/Signhost.APIClient.Tests/Signhost.APIClient.Tests.csproj b/src/Signhost.APIClient.Tests/Signhost.APIClient.Tests.csproj index 9d82d4b..41c1975 100644 --- a/src/Signhost.APIClient.Tests/Signhost.APIClient.Tests.csproj +++ b/src/Signhost.APIClient.Tests/Signhost.APIClient.Tests.csproj @@ -1,7 +1,8 @@ - net8.0 + net10.0 false + enable diff --git a/src/Signhost.APIClient.Tests/SignhostApiRetryClientTests.cs b/src/Signhost.APIClient.Tests/SignhostApiRetryClientTests.cs index 28881d2..68f222a 100644 --- a/src/Signhost.APIClient.Tests/SignhostApiRetryClientTests.cs +++ b/src/Signhost.APIClient.Tests/SignhostApiRetryClientTests.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.IO; using System.Net; @@ -14,425 +13,424 @@ using Signhost.APIClient.Rest.Polly; using Xunit; -namespace SignhostAPIRetryClient.Tests +namespace SignhostAPIRetryClient.Tests; + +/// +/// Tests the SignhostApiRetryClient. +/// +public class SignhostApiRetryClientTests + : IDisposable { + private readonly SignhostApiClientSettings settings; + + private readonly SignhostApiRetryClient firstCallSucceedsClient; + private readonly SignhostApiRetryClient thirdCallSucceedsClient; + private readonly SignhostApiRetryClient allCallsFailClient; + + private readonly MemoryStream documentFile; + + /// + /// Initializes a new instance of the class. + /// Sets up the mocked handlers for the HttpClient. + /// + public SignhostApiRetryClientTests() + { + settings = new SignhostApiClientSettings( + "AppKey") { + Endpoint = "http://localhost/api/", + }; + + firstCallSucceedsClient = new SignhostApiRetryClient( + settings, + new HttpClient( + SetupMockedHandler( + HttpStatusCode.OK, + HttpStatusCode.InternalServerError, + HttpStatusCode.InternalServerError, + HttpStatusCode.InternalServerError))); + + thirdCallSucceedsClient = new SignhostApiRetryClient( + settings, + new HttpClient( + SetupMockedHandler( + HttpStatusCode.InternalServerError, + HttpStatusCode.InternalServerError, + HttpStatusCode.OK, + HttpStatusCode.InternalServerError))); + + allCallsFailClient = new SignhostApiRetryClient( + settings, + new HttpClient( + SetupMockedHandler( + HttpStatusCode.InternalServerError, + HttpStatusCode.InternalServerError, + HttpStatusCode.InternalServerError, + HttpStatusCode.InternalServerError))); + + documentFile = new MemoryStream(); + } + + /// + /// Tests if the api call works as expected the first time around. + /// So the retry policy isn't used and no exception is thrown. + /// + [Fact] + public void When_First_Call_Succeeds_AddOrReplaceFileMetaToTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + firstCallSucceedsClient.AddOrReplaceFileMetaToTransactionAsync( + new FileMeta(), + "Transaction Id", + "File Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the first time around. + /// So the retry policy isn't used and no exception is thrown. + /// + [Fact] + public void When_First_Call_Succeeds_AddOrReplaceFileToTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + firstCallSucceedsClient.AddOrReplaceFileToTransactionAsync( + documentFile, + "Transaction Id", + "File Id", + new FileUploadOptions()); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the first time around. + /// So the retry policy isn't used and no exception is thrown. + /// + [Fact] + public void When_First_Call_Succeeds_CreateTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => firstCallSucceedsClient + .CreateTransactionAsync(new CreateTransactionRequest()); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the first time around. + /// So the retry policy isn't used and no exception is thrown. + /// + [Fact] + public void When_First_Call_Succeeds_DeleteTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + firstCallSucceedsClient.DeleteTransactionAsync( + "Transaction Id", + new DeleteTransactionOptions()); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the first time around. + /// So the retry policy isn't used and no exception is thrown. + /// + [Fact] + public void When_First_Call_Succeeds_GetDocumentAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + firstCallSucceedsClient.GetDocumentAsync( + "Transaction Id", + "File Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the first time around. + /// So the retry policy isn't used and no exception is thrown. + /// + [Fact] + public void When_First_Call_Succeeds_GetReceiptAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + firstCallSucceedsClient.GetReceiptAsync("Transaction Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the first time around. + /// So the retry policy isn't used and no exception is thrown. + /// + [Fact] + public void When_First_Call_Succeeds_GetTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + firstCallSucceedsClient.GetTransactionAsync("transaction Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the first time around. + /// So the retry policy isn't used and no exception is thrown. + /// + [Fact] + public void When_First_Call_Succeeds_GetTransactionResponseAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + firstCallSucceedsClient.GetTransactionResponseAsync("Transaction Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the third time around. + /// So the retry policy is used and no exception is thrown. + /// + [Fact] + public void When_Third_Call_Succeeds_AddOrReplaceFileMetaToTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () + => thirdCallSucceedsClient.AddOrReplaceFileMetaToTransactionAsync( + new FileMeta(), + "Transaction Id", + "File Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the third time around. + /// So the retry policy is used and no exception is thrown. + /// + [Fact] + public void When_Third_Call_Succeeds_AddOrReplaceFileToTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + thirdCallSucceedsClient.AddOrReplaceFileToTransactionAsync( + documentFile, + "Transaction Id", + "File Id", + new FileUploadOptions()); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the third time around. + /// So the retry policy is used and no exception is thrown. + /// + [Fact] + public void When_Third_Call_Succeeds_CreateTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => thirdCallSucceedsClient + .CreateTransactionAsync(new CreateTransactionRequest()); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the third time around. + /// So the retry policy is used and no exception is thrown. + /// + [Fact] + public void When_Third_Call_Succeeds_DeleteTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + thirdCallSucceedsClient.DeleteTransactionAsync( + "Transaction Id", + new DeleteTransactionOptions()); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the third time around. + /// So the retry policy is used and no exception is thrown. + /// + [Fact] + public void When_Third_Call_Succeeds_GetDocumentAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + thirdCallSucceedsClient.GetDocumentAsync( + "Transaction Id", + "File Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the third time around. + /// So the retry policy is used and no exception is thrown. + /// + [Fact] + public void When_Third_Call_Succeeds_GetReceiptAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + thirdCallSucceedsClient.GetReceiptAsync("Transaction Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the third time around. + /// So the retry policy is used and no exception is thrown. + /// + [Fact] + public void When_Third_Call_Succeeds_GetTransactionAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + thirdCallSucceedsClient.GetTransactionAsync("transaction Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call works as expected the third time around. + /// So the retry policy is used and no exception is thrown. + /// + [Fact] + public void When_Third_Call_Succeeds_GetTransactionResponseAsync_Should_Not_Throw_Exception() + { + Func apiCall = () => + thirdCallSucceedsClient.GetTransactionResponseAsync("Transaction Id"); + apiCall.Should().NotThrowAsync(); + } + + /// + /// Tests if the api call fails as expected the fourth time around. + /// So the retry policy is used and a exception is thrown. + /// + [Fact] + public void When_All_Calls_Fail_AddOrReplaceFileMetaToTransactionAsync_Should_Throw_Exception() + { + Func apiCall = () => + allCallsFailClient.AddOrReplaceFileMetaToTransactionAsync( + new FileMeta(), + "Transaction Id", + "File Id"); + apiCall.Should().ThrowAsync(); + } + + /// + /// Tests if the api call fails as expected the fourth time around. + /// So the retry policy is used and a exception is thrown. + /// + [Fact] + public void When_All_Calls_Fail_AddOrReplaceFileToTransactionAsync_Should_Throw_Exception() + { + Func apiCall = () => + allCallsFailClient.AddOrReplaceFileToTransactionAsync( + new MemoryStream(), + "Transaction Id", + "File Id", + new FileUploadOptions()); + apiCall.Should().ThrowAsync(); + } + + /// + /// Tests if the api call fails as expected the fourth time around. + /// So the retry policy is used and a exception is thrown. + /// + [Fact] + public void When_All_Calls_Fail_CreateTransactionAsync_Should_Throw_Exception() + { + Func apiCall = () => allCallsFailClient + .CreateTransactionAsync(new CreateTransactionRequest()); + apiCall.Should().ThrowAsync(); + } + + /// + /// Tests if the api call fails as expected the fourth time around. + /// So the retry policy is used and a exception is thrown. + /// + [Fact] + public void When_All_Calls_Fail_DeleteTransactionAsync_Should_Throw_Exception() + { + Func apiCall = () => + allCallsFailClient.DeleteTransactionAsync( + "Transaction Id", + new DeleteTransactionOptions()); + apiCall.Should().ThrowAsync(); + } + + /// + /// Tests if the api call fails as expected the fourth time around. + /// So the retry policy is used and a exception is thrown. + /// + [Fact] + public void When_All_Calls_Fails_GetDocumentAsync_Should_Throw_Exception() + { + Func apiCall = () => + allCallsFailClient.GetDocumentAsync( + "Transaction Id", + "File Id"); + apiCall.Should().ThrowAsync(); + } + + /// + /// Tests if the api call fails as expected the fourth time around. + /// So the retry policy is used and a exception is thrown. + /// + [Fact] + public void When_All_Calls_Fail_GetReceiptAsync_Should_Throw_Exception() + { + Func apiCall = () => + allCallsFailClient.GetReceiptAsync("Transaction Id"); + apiCall.Should().ThrowAsync(); + } + /// - /// Tests the SignHostApiRetryClient. + /// Tests if the api call fails as expected the fourth time around. + /// So the retry policy is used and a exception is thrown. /// - public class SignhostApiRetryClientTests - : IDisposable + [Fact] + public void When_All_Calls_Fail_GetTransactionAsync_Should_Throw_Exception() + { + Func apiCall = () => + allCallsFailClient.GetTransactionAsync("transaction Id"); + apiCall.Should().ThrowAsync(); + } + + /// + /// Tests if the api call fails as expected the fourth time around. + /// So the retry policy is used and a exception is thrown. + /// + [Fact] + public void When_All_Calls_Fail_GetTransactionResponseAsync_Should_Throw_Exception() + { + Func apiCall = () => + allCallsFailClient.GetTransactionResponseAsync("Transaction Id"); + apiCall.Should().ThrowAsync(); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes of the objects in the class, + /// that implement IDisposable. + /// + /// If set to true then the object is being disposed from a call to Dispose(); + /// false if it is from a finalizer / destructor. + protected virtual void Dispose(bool disposing) + { + firstCallSucceedsClient.Dispose(); + thirdCallSucceedsClient.Dispose(); + allCallsFailClient.Dispose(); + + documentFile.Dispose(); + } + + private HttpMessageHandler SetupMockedHandler( + HttpStatusCode firstStatus, + HttpStatusCode secondStatus, + HttpStatusCode thirdStatus, + HttpStatusCode fourthStatus) { - private readonly SignHostApiClientSettings settings; - - private readonly SignHostApiRetryClient firstCallSucceedsClient; - private readonly SignHostApiRetryClient thirdCallSucceedsClient; - private readonly SignHostApiRetryClient allCallsFailClient; - - private readonly MemoryStream documentFile; - - /// - /// Initializes a new instance of the class. - /// Sets up the mocked handlers for the HttpClient. - /// - public SignhostApiRetryClientTests() - { - settings = new SignHostApiClientSettings( - "AppKey") { - Endpoint = "http://localhost/api/", - }; - - firstCallSucceedsClient = new SignHostApiRetryClient( - settings, - new HttpClient( - SetupMockedHandler( - HttpStatusCode.OK, - HttpStatusCode.InternalServerError, - HttpStatusCode.InternalServerError, - HttpStatusCode.InternalServerError))); - - thirdCallSucceedsClient = new SignHostApiRetryClient( - settings, - new HttpClient( - SetupMockedHandler( - HttpStatusCode.InternalServerError, - HttpStatusCode.InternalServerError, - HttpStatusCode.OK, - HttpStatusCode.InternalServerError))); - - allCallsFailClient = new SignHostApiRetryClient( - settings, - new HttpClient( - SetupMockedHandler( - HttpStatusCode.InternalServerError, - HttpStatusCode.InternalServerError, - HttpStatusCode.InternalServerError, - HttpStatusCode.InternalServerError))); - - documentFile = new MemoryStream(); - } - - /// - /// Tests if the api call works as expected the first time around. - /// So the retry policy isn't used and no exception is thrown. - /// - [Fact] - public void When_First_Call_Succeeds_AddOrReplaceFileMetaToTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - firstCallSucceedsClient.AddOrReplaceFileMetaToTransactionAsync( - new FileMeta(), - "Transaction Id", - "File Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the first time around. - /// So the retry policy isn't used and no exception is thrown. - /// - [Fact] - public void When_First_Call_Succeeds_AddOrReplaceFileToTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - firstCallSucceedsClient.AddOrReplaceFileToTransactionAsync( - documentFile, - "Transaction Id", - "File Id", - new FileUploadOptions()); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the first time around. - /// So the retry policy isn't used and no exception is thrown. - /// - [Fact] - public void When_First_Call_Succeeds_CreateTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - firstCallSucceedsClient.CreateTransactionAsync(new Transaction()); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the first time around. - /// So the retry policy isn't used and no exception is thrown. - /// - [Fact] - public void When_First_Call_Succeeds_DeleteTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - firstCallSucceedsClient.DeleteTransactionAsync( - "Transaction Id", - new DeleteTransactionOptions()); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the first time around. - /// So the retry policy isn't used and no exception is thrown. - /// - [Fact] - public void When_First_Call_Succeeds_GetDocumentAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - firstCallSucceedsClient.GetDocumentAsync( - "Transaction Id", - "File Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the first time around. - /// So the retry policy isn't used and no exception is thrown. - /// - [Fact] - public void When_First_Call_Succeeds_GetReceiptAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - firstCallSucceedsClient.GetReceiptAsync("Transaction Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the first time around. - /// So the retry policy isn't used and no exception is thrown. - /// - [Fact] - public void When_First_Call_Succeeds_GetTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - firstCallSucceedsClient.GetTransactionAsync("transaction Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the first time around. - /// So the retry policy isn't used and no exception is thrown. - /// - [Fact] - public void When_First_Call_Succeeds_GetTransactionResponseAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - firstCallSucceedsClient.GetTransactionResponseAsync("Transaction Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the third time around. - /// So the retry policy is used and no exception is thrown. - /// - [Fact] - public void When_Third_Call_Succeeds_AddOrReplaceFileMetaToTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () - => thirdCallSucceedsClient.AddOrReplaceFileMetaToTransactionAsync( - new FileMeta(), - "Transaction Id", - "File Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the third time around. - /// So the retry policy is used and no exception is thrown. - /// - [Fact] - public void When_Third_Call_Succeeds_AddOrReplaceFileToTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - thirdCallSucceedsClient.AddOrReplaceFileToTransactionAsync( - documentFile, - "Transaction Id", - "File Id", - new FileUploadOptions()); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the third time around. - /// So the retry policy is used and no exception is thrown. - /// - [Fact] - public void When_Third_Call_Succeeds_CreateTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - thirdCallSucceedsClient.CreateTransactionAsync(new Transaction()); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the third time around. - /// So the retry policy is used and no exception is thrown. - /// - [Fact] - public void When_Third_Call_Succeeds_DeleteTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - thirdCallSucceedsClient.DeleteTransactionAsync( - "Transaction Id", - new DeleteTransactionOptions()); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the third time around. - /// So the retry policy is used and no exception is thrown. - /// - [Fact] - public void When_Third_Call_Succeeds_GetDocumentAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - thirdCallSucceedsClient.GetDocumentAsync( - "Transaction Id", - "File Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the third time around. - /// So the retry policy is used and no exception is thrown. - /// - [Fact] - public void When_Third_Call_Succeeds_GetReceiptAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - thirdCallSucceedsClient.GetReceiptAsync("Transaction Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the third time around. - /// So the retry policy is used and no exception is thrown. - /// - [Fact] - public void When_Third_Call_Succeeds_GetTransactionAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - thirdCallSucceedsClient.GetTransactionAsync("transaction Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call works as expected the third time around. - /// So the retry policy is used and no exception is thrown. - /// - [Fact] - public void When_Third_Call_Succeeds_GetTransactionResponseAsync_Should_Not_Throw_Exception() - { - Func apiCall = () => - thirdCallSucceedsClient.GetTransactionResponseAsync("Transaction Id"); - apiCall.Should().NotThrowAsync(); - } - - /// - /// Tests if the api call fails as expected the fourth time around. - /// So the retry policy is used and a exception is thrown. - /// - [Fact] - public void When_All_Calls_Fail_AddOrReplaceFileMetaToTransactionAsync_Should_Throw_Exception() - { - Func apiCall = () => - allCallsFailClient.AddOrReplaceFileMetaToTransactionAsync( - new FileMeta(), - "Transaction Id", - "File Id"); - apiCall.Should().ThrowAsync(); - } - - /// - /// Tests if the api call fails as expected the fourth time around. - /// So the retry policy is used and a exception is thrown. - /// - [Fact] - public void When_All_Calls_Fail_AddOrReplaceFileToTransactionAsync_Should_Throw_Exception() - { - Func apiCall = () => - allCallsFailClient.AddOrReplaceFileToTransactionAsync( - new MemoryStream(), - "Transaction Id", - "File Id", - new FileUploadOptions()); - apiCall.Should().ThrowAsync(); - } - - /// - /// Tests if the api call fails as expected the fourth time around. - /// So the retry policy is used and a exception is thrown. - /// - [Fact] - public void When_All_Calls_Fail_CreateTransactionAsync_Should_Throw_Exception() - { - Func apiCall = () => - allCallsFailClient.CreateTransactionAsync(new Transaction()); - apiCall.Should().ThrowAsync(); - } - - /// - /// Tests if the api call fails as expected the fourth time around. - /// So the retry policy is used and a exception is thrown. - /// - [Fact] - public void When_All_Calls_Fail_DeleteTransactionAsync_Should_Throw_Exception() - { - Func apiCall = () => - allCallsFailClient.DeleteTransactionAsync( - "Transaction Id", - new DeleteTransactionOptions()); - apiCall.Should().ThrowAsync(); - } - - /// - /// Tests if the api call fails as expected the fourth time around. - /// So the retry policy is used and a exception is thrown. - /// - [Fact(Skip = "Current method of the base library hasn't implemented SignhostRestApiClientException yet.")] - public void When_All_Calls_Fails_GetDocumentAsync_Should_Throw_Exception() - { - Func apiCall = () => - allCallsFailClient.GetDocumentAsync( - "Transaction Id", - "File Id"); - apiCall.Should().ThrowAsync(); - } - - /// - /// Tests if the api call fails as expected the fourth time around. - /// So the retry policy is used and a exception is thrown. - /// - [Fact(Skip = "Current method of the base library hasn't implemented SignhostRestApiClientException yet.")] - public void When_All_Calls_Fail_GetReceiptAsync_Should_Throw_Exception() - { - Func apiCall = () => - allCallsFailClient.GetReceiptAsync("Transaction Id"); - apiCall.Should().ThrowAsync(); - } - - /// - /// Tests if the api call fails as expected the fourth time around. - /// So the retry policy is used and a exception is thrown. - /// - [Fact] - public void When_All_Calls_Fail_GetTransactionAsync_Should_Throw_Exception() - { - Func apiCall = () => - allCallsFailClient.GetTransactionAsync("transaction Id"); - apiCall.Should().ThrowAsync(); - } - - /// - /// Tests if the api call fails as expected the fourth time around. - /// So the retry policy is used and a exception is thrown. - /// - [Fact] - public void When_All_Calls_Fail_GetTransactionResponseAsync_Should_Throw_Exception() - { - Func apiCall = () => - allCallsFailClient.GetTransactionResponseAsync("Transaction Id"); - apiCall.Should().ThrowAsync(); - } - - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Disposes of the objects in the class, - /// that implement IDisposable. - /// - /// If set to true then the object is being disposed from a call to Dispose(); - /// false if it is from a finalizer / destructor. - protected virtual void Dispose(bool disposing) - { - firstCallSucceedsClient.Dispose(); - thirdCallSucceedsClient.Dispose(); - allCallsFailClient.Dispose(); - - documentFile.Dispose(); - } - - private HttpMessageHandler SetupMockedHandler( - HttpStatusCode firstStatus, - HttpStatusCode secondStatus, - HttpStatusCode thirdStatus, - HttpStatusCode fourthStatus) - { - var mockHandler = new Mock(); - mockHandler - .Protected() - .SetupSequence>( - "SendAsync", - ItExpr.IsAny(), - ItExpr.IsAny()) - .ReturnsAsync(new HttpResponseMessage(statusCode: firstStatus)) - .ReturnsAsync(new HttpResponseMessage(statusCode: secondStatus)) - .ReturnsAsync(new HttpResponseMessage(statusCode: thirdStatus)) - .ReturnsAsync(new HttpResponseMessage(statusCode: fourthStatus)); - - return mockHandler.Object; - } + var mockHandler = new Mock(); + mockHandler + .Protected() + .SetupSequence>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage(statusCode: firstStatus)) + .ReturnsAsync(new HttpResponseMessage(statusCode: secondStatus)) + .ReturnsAsync(new HttpResponseMessage(statusCode: thirdStatus)) + .ReturnsAsync(new HttpResponseMessage(statusCode: fourthStatus)); + + return mockHandler.Object; } } diff --git a/src/Signhost.APIClient/Rest/Polly/SignHostAPIRetryClient.cs b/src/Signhost.APIClient/Rest/Polly/SignHostAPIRetryClient.cs index e08da3a..3af4cac 100644 --- a/src/Signhost.APIClient/Rest/Polly/SignHostAPIRetryClient.cs +++ b/src/Signhost.APIClient/Rest/Polly/SignHostAPIRetryClient.cs @@ -1,5 +1,4 @@ -#nullable enable -using System; +using System; using System.IO; using System.Net.Http; using System.Threading; @@ -8,323 +7,212 @@ using Signhost.APIClient.Rest.DataObjects; using Signhost.APIClient.Rest.ErrorHandling; -namespace Signhost.APIClient.Rest.Polly +namespace Signhost.APIClient.Rest.Polly; + +/// +/// Implements the interface which provides +/// a signhost api client implementation. +/// +public class SignhostApiRetryClient + : ISignhostApiClient + , IDisposable { + private readonly SignhostApiClient client; + private readonly AsyncPolicy retryPolicy; + /// - /// Implements the interface which provides - /// a signhost api client implementation. + /// Initializes a new instance of the class. + /// This client has a built-in retry mechanism. + /// Set your usertoken and APPKey by creating a . /// - public class SignHostApiRetryClient - : ISignHostApiClient - , IDisposable + /// . + /// An to use instead of the default. + public SignhostApiRetryClient( + ISignhostApiClientSettings settings, + AsyncPolicy? policy = null) + : this(settings, new HttpClient(), policy) { - private readonly SignHostApiClient client; - private readonly AsyncPolicy retryPolicy; - - /// - /// Initializes a new instance of the class. - /// This client has a built-in retry mechanism. - /// Set your usertoken and APPKey by creating a . - /// - /// . - /// An to use instead of the default. - public SignHostApiRetryClient( - ISignHostApiClientSettings settings, - AsyncPolicy? policy = null) - : this(settings, new HttpClient(), policy) - { - } + } - /// - /// Initializes a new instance of the class. - /// This client has a built-in retry mechanism. - /// Set your usertoken and APPKey by creating a . - /// - /// . - /// to use for all http calls. - /// An to use instead of the default. - public SignHostApiRetryClient( - ISignHostApiClientSettings settings, - HttpClient httpClient, - AsyncPolicy? policy = null) - { - client = new SignHostApiClient(settings, httpClient); + /// + /// Initializes a new instance of the class. + /// This client has a built-in retry mechanism. + /// Set your usertoken and APPKey by creating a . + /// + /// . + /// to use for all http calls. + /// An to use instead of the default. + public SignhostApiRetryClient( + ISignhostApiClientSettings settings, + HttpClient httpClient, + AsyncPolicy? policy = null) + { + client = new SignhostApiClient(settings, httpClient); - if (policy is null) { - retryPolicy = GetDefaultPolicy(); - } - else { - retryPolicy = policy; - } + if (policy is null) { + retryPolicy = GetDefaultPolicy(); + } + else { + retryPolicy = policy; } + } + + /// + public async Task CreateTransactionAsync( + CreateTransactionRequest request, + CancellationToken cancellationToken = default) + { + return await retryPolicy.ExecuteAsync( + ct => client.CreateTransactionAsync(request, ct), + cancellationToken); + } - /// - public async Task AddOrReplaceFileMetaToTransactionAsync( - FileMeta fileMeta, - string transactionId, - string fileId) - => await AddOrReplaceFileMetaToTransactionAsync( + /// + public async Task AddOrReplaceFileMetaToTransactionAsync( + FileMeta fileMeta, + string transactionId, + string fileId, + CancellationToken cancellationToken = default) + { + await retryPolicy.ExecuteAsync( + ct => client.AddOrReplaceFileMetaToTransactionAsync( fileMeta, transactionId, fileId, - default); - - /// - public async Task AddOrReplaceFileMetaToTransactionAsync( - FileMeta fileMeta, - string transactionId, - string fileId, - CancellationToken cancellationToken = default) - { - await retryPolicy - .ExecuteAsync( - ct => client.AddOrReplaceFileMetaToTransactionAsync( - fileMeta, - transactionId, - fileId, - ct), - cancellationToken); - } + ct), + cancellationToken); + } - /// - public async Task AddOrReplaceFileToTransactionAsync( - Stream fileStream, - string transactionId, - string fileId, - FileUploadOptions uploadOptions) - => await AddOrReplaceFileToTransactionAsync( + /// + public async Task AddOrReplaceFileToTransactionAsync( + Stream fileStream, + string transactionId, + string fileId, + FileUploadOptions? uploadOptions, + CancellationToken cancellationToken = default) + { + await retryPolicy.ExecuteAsync( + ct => client.AddOrReplaceFileToTransactionAsync( fileStream, transactionId, fileId, uploadOptions, - default); - - /// - public async Task AddOrReplaceFileToTransactionAsync( - Stream fileStream, - string transactionId, - string fileId, - FileUploadOptions uploadOptions, - CancellationToken cancellationToken = default) - { - await retryPolicy - .ExecuteAsync( - ct => client.AddOrReplaceFileToTransactionAsync( - fileStream, - transactionId, - fileId, - uploadOptions, - ct), - cancellationToken); - } + ct), + cancellationToken); + } - /// - public async Task AddOrReplaceFileToTransactionAsync( - string filePath, - string transactionId, - string fileId, - FileUploadOptions uploadOptions) - => await AddOrReplaceFileToTransactionAsync( + /// + public async Task AddOrReplaceFileToTransactionAsync( + string filePath, + string transactionId, + string fileId, + FileUploadOptions? uploadOptions, + CancellationToken cancellationToken = default) + { + await retryPolicy.ExecuteAsync( + ct => client.AddOrReplaceFileToTransactionAsync( filePath, transactionId, fileId, uploadOptions, - default); - - /// - public async Task AddOrReplaceFileToTransactionAsync( - string filePath, - string transactionId, - string fileId, - FileUploadOptions uploadOptions, - CancellationToken cancellationToken = default) - { - await retryPolicy - .ExecuteAsync( - ct => client.AddOrReplaceFileToTransactionAsync( - filePath, - transactionId, - fileId, - uploadOptions, - ct), - cancellationToken); - } - - /// - public async Task CreateTransactionAsync( - Transaction transaction) - => await CreateTransactionAsync(transaction, default); - - /// - public async Task CreateTransactionAsync( - Transaction transaction, - CancellationToken cancellationToken = default) - { - return await retryPolicy - .ExecuteAsync( - ct => client.CreateTransactionAsync( - transaction, - ct), - cancellationToken); - } - - /// - public async Task DeleteTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default) - => await DeleteTransactionAsync( - transactionId, - default, - cancellationToken); - - /// - public async Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions options) - => await DeleteTransactionAsync( - transactionId, - options, - default); - - /// - public async Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions? options = null, - CancellationToken cancellationToken = default) - { - await retryPolicy - .ExecuteAsync( - ct => client.DeleteTransactionAsync( - transactionId, - options, - ct), - cancellationToken); - } - - /// - public async Task GetDocumentAsync( - string transactionId, - string fileId) - => await GetDocumentAsync(transactionId, fileId, default); - - /// - public async Task GetDocumentAsync( - string transactionId, - string fileId, - CancellationToken cancellationToken = default) - { - return await retryPolicy - .ExecuteAsync( - ct => client.GetDocumentAsync( - transactionId, - fileId, - ct), - cancellationToken); - } - - /// - public async Task GetReceiptAsync(string transactionId) - => await GetReceiptAsync(transactionId, default); - - /// - public async Task GetReceiptAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - return await retryPolicy - .ExecuteAsync( - ct => client.GetReceiptAsync( - transactionId, - ct), - cancellationToken); - } + ct), + cancellationToken); + } - /// - public async Task GetTransactionAsync(string transactionId) - => await GetTransactionAsync(transactionId, default); + /// + public async Task DeleteTransactionAsync( + string transactionId, + DeleteTransactionOptions? options = null, + CancellationToken cancellationToken = default) + { + await retryPolicy.ExecuteAsync( + ct => client.DeleteTransactionAsync(transactionId, options, ct), + cancellationToken); + } - /// - public async Task GetTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - return await retryPolicy - .ExecuteAsync( - ct => client.GetTransactionAsync( - transactionId, - ct), - cancellationToken); - } + /// + public async Task GetDocumentAsync( + string transactionId, + string fileId, + CancellationToken cancellationToken = default) + { + return await retryPolicy.ExecuteAsync( + ct => client.GetDocumentAsync(transactionId, fileId, ct), + cancellationToken); + } - /// - public async Task> GetTransactionResponseAsync( - string transactionId) - => await GetTransactionResponseAsync(transactionId, default); + /// + public async Task GetReceiptAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + return await retryPolicy.ExecuteAsync( + ct => client.GetReceiptAsync(transactionId, ct), + cancellationToken); + } - /// - public async Task> GetTransactionResponseAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - return await retryPolicy - .ExecuteAsync( - ct => client.GetTransactionResponseAsync( - transactionId, - ct), - cancellationToken); - } + /// + public async Task GetTransactionAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + return await retryPolicy.ExecuteAsync( + ct => client.GetTransactionAsync(transactionId, ct), + cancellationToken); + } - /// - public async Task StartTransactionAsync( - string transactionId) - => await StartTransactionAsync(transactionId, default); + /// + public async Task> GetTransactionResponseAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + return await retryPolicy.ExecuteAsync( + ct => client.GetTransactionResponseAsync(transactionId, ct), + cancellationToken); + } - /// - public async Task StartTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - await retryPolicy - .ExecuteAsync( - ct => client.StartTransactionAsync( - transactionId, - ct), - cancellationToken); - } + /// + public async Task StartTransactionAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + await retryPolicy.ExecuteAsync( + ct => client.StartTransactionAsync(transactionId, ct), + cancellationToken); + } - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - /// - /// Disposes the instance. - /// - /// Is callled. - protected virtual void Dispose(bool disposing) - { - if (disposing) { - client?.Dispose(); - } + /// + /// Disposes the instance. + /// + /// Is callled. + protected virtual void Dispose(bool disposing) + { + if (disposing) { + client?.Dispose(); } + } - private static AsyncPolicy GetDefaultPolicy() - { - return Policy - .Handle(ex => - !(ex is BadAuthorizationException) && - !(ex is BadRequestException) && - !(ex is NotFoundException)) - - // When an HttpClient times out it doesn't throw a TimeoutException like you'd expect. - // Instead it throws a TaskCanceledException, that's why we check the cancellation token. - .Or(ex => !ex.CancellationToken.IsCancellationRequested) - .WaitAndRetryAsync( - 3, - retryAttempt => - TimeSpan.FromMilliseconds(Math.Pow(10, retryAttempt))); - } + private static AsyncPolicy GetDefaultPolicy() + { + return Policy + .Handle(ex => + ex is not BadAuthorizationException && + ex is not BadRequestException && + ex is not NotFoundException) + + // When an HttpClient times out it doesn't throw a TimeoutException like you'd expect. + // Instead it throws a TaskCanceledException, that's why we check the cancellation token. + .Or(ex => !ex.CancellationToken.IsCancellationRequested) + .WaitAndRetryAsync( + 3, + retryAttempt => + TimeSpan.FromMilliseconds(Math.Pow(10, retryAttempt))); } } diff --git a/src/Signhost.APIClient/Signhost.APIClient.csproj b/src/Signhost.APIClient/Signhost.APIClient.csproj index 0f9b5c3..96b5568 100644 --- a/src/Signhost.APIClient/Signhost.APIClient.csproj +++ b/src/Signhost.APIClient/Signhost.APIClient.csproj @@ -1,7 +1,9 @@  - netstandard2.0 + net10.0;net9.0;net8.0;netstandard2.0;net462 + 10 Signhost.APIClient + enable @@ -44,6 +46,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - +