Skip to content

Commit

Permalink
Merge pull request #43 from Lombiq/issue/NEST-496
Browse files Browse the repository at this point in the history
NEST-496: Tenant API error detection, monitoring and handling
  • Loading branch information
sarahelsaig authored Jan 18, 2024
2 parents e80a011 + 8979670 commit 350f0ce
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 12 deletions.
40 changes: 39 additions & 1 deletion Lombiq.OrchardCoreApiClient/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Lombiq.OrchardCoreApiClient.Exceptions;
using Lombiq.OrchardCoreApiClient.Interfaces;
using Lombiq.OrchardCoreApiClient.Models;
using Polly;
using Polly.Retry;
using Refit;
using System;
using System.Net.Http;
Expand All @@ -25,9 +27,12 @@ public class ApiClient<TApi> : IDisposable

private HttpClient _httpClient;

public AsyncRetryPolicy RetryPolicy { get; set; }

public TApi OrchardCoreApi => _lazyOrchardCoreApi.Value;

public ApiClient(ApiClientSettings apiClientSettings) =>
public ApiClient(ApiClientSettings apiClientSettings)
{
_lazyOrchardCoreApi = new(() =>
{
_httpClient = ConfigurableCertificateValidatingHttpClientHandler.CreateClient(apiClientSettings);
Expand All @@ -36,6 +41,39 @@ public ApiClient(ApiClientSettings apiClientSettings) =>
return RefitHelper.WithNewtonsoftJson<TApi>(_httpClient);
});

RetryPolicy = InitRetryPolicy();
}

public AsyncRetryPolicy InitRetryPolicy(
int retryCount = 3,
Func<int, TimeSpan> sleepDurationProvider = null,
Func<Exception, TimeSpan, int, Context, Task> onRetryAsync = null) =>
Policy
.Handle<ApiException>()
.Or<ApiClientException>()
.Or<HttpRequestException>()
.WaitAndRetryAsync(
retryCount,
sleepDurationProvider ?? (_ => TimeSpan.FromSeconds(2)),
onRetryAsync ?? ((_, _, _, _) => Task.CompletedTask));

public async Task<TResult> ExecuteWithRetryPolicyAsync<TResult>(
Func<Task<TResult>> executeAction,
Func<ApiClientException, Task> catchAction)
{
try
{
return await RetryPolicy.ExecuteAsync(executeAction);
}
catch (ApiClientException ex)
{
await catchAction(ex);

// Throw the exception again so the caller can handle it as well.
throw;
}
}

public async Task CreateAndSetupTenantAsync(
TenantApiModel createApiViewModel,
TenantSetupApiModel setupApiViewModel)
Expand Down
1 change: 1 addition & 0 deletions Lombiq.OrchardCoreApiClient/Constants/CommonHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
public static class CommonHeaders
{
public const string AuthorizationBearer = "Authorization: Bearer";
public const string RequestedWithXmlHttpRequest = "X-Requested-With: XMLHttpRequest";
}
22 changes: 11 additions & 11 deletions Lombiq.OrchardCoreApiClient/Interfaces/IOrchardCoreApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public interface IOrchardCoreApi
/// </param>
/// <returns>The response of the tenant creation.</returns>
[Post("/api/tenants/create/")]
[Headers(AuthorizationBearer)]
[Headers(AuthorizationBearer, RequestedWithXmlHttpRequest)]
Task<ApiResponse<string>> CreateAsync([Body(buffered: true)] TenantApiModel createTenantParameters);

/// <summary>
Expand All @@ -29,42 +29,42 @@ public interface IOrchardCoreApi
/// </param>
/// <returns>The response of the tenant setup.</returns>
[Post("/api/tenants/setup")]
[Headers(AuthorizationBearer)]
Task<string> SetupAsync([Body(buffered: true)] TenantSetupApiModel setupTenantParameters);
[Headers(AuthorizationBearer, RequestedWithXmlHttpRequest)]
Task<ApiResponse<string>> SetupAsync([Body(buffered: true)] TenantSetupApiModel setupTenantParameters);

/// <summary>
/// Edit a previously created tenant in Orchard Core.
/// </summary>
/// <param name="editTenantParameters">The necessary parameter to edit a tenant: Name.</param>
/// <returns>The response of the tenant edit.</returns>
[Post("/api/tenants/edit")]
[Headers(AuthorizationBearer)]
Task<string> EditAsync([Body(buffered: true)] TenantApiModel editTenantParameters);
[Headers(AuthorizationBearer, RequestedWithXmlHttpRequest)]
Task<ApiResponse<string>> EditAsync([Body(buffered: true)] TenantApiModel editTenantParameters);

/// <summary>
/// Remove a previously created tenant in Orchard Core.
/// </summary>
/// <param name="tenantName">The necessary parameter to remove a tenant.</param>
/// <returns>The response of the tenant removal.</returns>
[Post("/api/tenants/remove/{tenantName}")]
[Headers(AuthorizationBearer)]
Task<string> RemoveAsync(string tenantName);
[Headers(AuthorizationBearer, RequestedWithXmlHttpRequest)]
Task<ApiResponse<string>> RemoveAsync(string tenantName);

/// <summary>
/// Disable a previously created tenant in Orchard Core.
/// </summary>
/// <param name="tenantName">The necessary parameter to disable a tenant.</param>
/// <returns>The response of the tenant disable.</returns>
[Post("/api/tenants/disable/{tenantName}")]
[Headers(AuthorizationBearer)]
Task<string> DisableAsync(string tenantName);
[Headers(AuthorizationBearer, RequestedWithXmlHttpRequest)]
Task<ApiResponse<string>> DisableAsync(string tenantName);

/// <summary>
/// Enable a previously disabled tenant in Orchard Core.
/// </summary>
/// <param name="tenantName">The necessary parameter to enable a tenant.</param>
/// <returns>The response of the tenant enable.</returns>
[Post("/api/tenants/enable/{tenantName}")]
[Headers(AuthorizationBearer)]
Task<string> EnableAsync(string tenantName);
[Headers(AuthorizationBearer, RequestedWithXmlHttpRequest)]
Task<ApiResponse<string>> EnableAsync(string tenantName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NodaTime" Version="3.1.9" />
<PackageReference Include="Polly" Version="8.2.0" />
</ItemGroup>

<ItemGroup Condition="'$(NuGetBuild)' != 'true' and Exists($(LombiqHelpfulLibrariesPath))">
Expand Down

0 comments on commit 350f0ce

Please sign in to comment.