Skip to content

Psp 1362 #4609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/backend/api/Pims.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Polly.Extensions" Version="8.5.1" />
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1" />
<PackageReference Include="prometheus-net.AspNetCore.HealthChecks" Version="8.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
Expand Down
7 changes: 5 additions & 2 deletions source/backend/api/Repositories/Cdogs/CdogsAuthRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Pims.Api.Models.CodeTypes;
using Pims.Api.Models.Requests.Http;
using Pims.Core.Api.Exceptions;
using Polly.Registry;

namespace Pims.Api.Repositories.Cdogs
{
Expand All @@ -28,12 +29,14 @@ public class CdogsAuthRepository : CdogsBaseRepository, IDocumentGenerationAuthR
/// <param name="httpClientFactory">Injected Httpclient factory.</param>
/// <param name="configuration">The injected configuration provider.</param>
/// <param name="jsonOptions">The jsonOptions.</param>
/// <param name="pollyPipelineProvider">The polly retry policy.</param>
public CdogsAuthRepository(
ILogger<CdogsAuthRepository> logger,
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
IOptions<JsonSerializerOptions> jsonOptions)
: base(logger, httpClientFactory, configuration, jsonOptions)
IOptions<JsonSerializerOptions> jsonOptions,
ResiliencePipelineProvider<string> pollyPipelineProvider)
: base(logger, httpClientFactory, configuration, jsonOptions, pollyPipelineProvider)
{
_currentToken = null;
_lastSucessfullRequest = DateTime.UnixEpoch;
Expand Down
7 changes: 5 additions & 2 deletions source/backend/api/Repositories/Cdogs/CdogsBaseRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Extensions.Options;
using Pims.Api.Models.Config;
using Pims.Core.Api.Repositories.Rest;
using Polly.Registry;

namespace Pims.Api.Repositories.Cdogs
{
Expand All @@ -24,12 +25,14 @@ public abstract class CdogsBaseRepository : BaseRestRepository
/// <param name="httpClientFactory">Injected Httpclient factory.</param>
/// <param name="configuration">The injected configuration provider.</param>
/// <param name="jsonOptions">The json options.</param>
/// <param name="pollyPipelineProvider">The polly retry policy.</param>
protected CdogsBaseRepository(
ILogger logger,
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
IOptions<JsonSerializerOptions> jsonOptions)
: base(logger, httpClientFactory, jsonOptions)
IOptions<JsonSerializerOptions> jsonOptions,
ResiliencePipelineProvider<string> pollyPipelineProvider)
: base(logger, httpClientFactory, jsonOptions, pollyPipelineProvider)
{
_config = new CdogsConfig();
configuration.Bind(CdogsConfigSectionKey, _config);
Expand Down
7 changes: 5 additions & 2 deletions source/backend/api/Repositories/Cdogs/CdogsRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Pims.Api.Models.Cdogs;
using Pims.Api.Models.CodeTypes;
using Pims.Api.Models.Requests.Http;
using Polly.Registry;

namespace Pims.Api.Repositories.Cdogs
{
Expand All @@ -32,13 +33,15 @@ public class CdogsRepository : CdogsBaseRepository, IDocumentGenerationRepositor
/// <param name="authRepository">Injected repository that handles authentication.</param>
/// <param name="configuration">The injected configuration provider.</param>
/// <param name="jsonOptions">The jsonOptions.</param>
/// <param name="pollyPipelineProvider">The polly retry policy.</param>
public CdogsRepository(
ILogger<CdogsRepository> logger,
IHttpClientFactory httpClientFactory,
IDocumentGenerationAuthRepository authRepository,
IConfiguration configuration,
IOptions<JsonSerializerOptions> jsonOptions)
: base(logger, httpClientFactory, configuration, jsonOptions)
IOptions<JsonSerializerOptions> jsonOptions,
ResiliencePipelineProvider<string> pollyPipelineProvider)
: base(logger, httpClientFactory, configuration, jsonOptions, pollyPipelineProvider)
{
_authRepository = authRepository;
}
Expand Down
7 changes: 5 additions & 2 deletions source/backend/api/Repositories/Mayan/MayanAuthRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Pims.Api.Models.Mayan;
using Pims.Api.Models.Requests.Http;
using Pims.Core.Api.Exceptions;
using Polly.Registry;

namespace Pims.Api.Repositories.Mayan
{
Expand All @@ -28,12 +29,14 @@ public class MayanAuthRepository : MayanBaseRepository, IEdmsAuthRepository
/// <param name="httpClientFactory">Injected Httpclient factory.</param>
/// <param name="configuration">The injected configuration provider.</param>
/// <param name="jsonOptions">The jsonOptions.</param>
/// <param name="pollyPipelineProvider">The polly retry policy.</param>
public MayanAuthRepository(
ILogger<MayanAuthRepository> logger,
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
IOptions<JsonSerializerOptions> jsonOptions)
: base(logger, httpClientFactory, configuration, jsonOptions)
IOptions<JsonSerializerOptions> jsonOptions,
ResiliencePipelineProvider<string> pollyPipelineProvider)
: base(logger, httpClientFactory, configuration, jsonOptions, pollyPipelineProvider)
{
_currentToken = string.Empty;
}
Expand Down
7 changes: 5 additions & 2 deletions source/backend/api/Repositories/Mayan/MayanBaseRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.Options;
using Pims.Api.Models.Config;
using Pims.Core.Api.Repositories.Rest;
using Polly.Registry;

namespace Pims.Api.Repositories.Mayan
{
Expand All @@ -25,12 +26,14 @@ public abstract class MayanBaseRepository : BaseRestRepository
/// <param name="httpClientFactory">Injected Httpclient factory.</param>
/// <param name="configuration">The injected configuration provider.</param>
/// <param name="jsonOptions">The injected json options.</param>
/// <param name="pollyPipelineProvider">The polly retry policy.</param>
protected MayanBaseRepository(
ILogger logger,
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
IOptions<JsonSerializerOptions> jsonOptions)
: base(logger, httpClientFactory, jsonOptions)
IOptions<JsonSerializerOptions> jsonOptions,
ResiliencePipelineProvider<string> pollyPipelineProvider)
: base(logger, httpClientFactory, jsonOptions, pollyPipelineProvider)
{
_config = new MayanConfig();
configuration.Bind(MayanConfigSectionKey, _config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Pims.Api.Models.Mayan.Document;
using Pims.Api.Models.Mayan.Metadata;
using Pims.Api.Models.Requests.Http;
using Polly.Registry;

namespace Pims.Api.Repositories.Mayan
{
Expand All @@ -38,13 +39,15 @@ public class MayanDocumentRepository : MayanBaseRepository, IEdmsDocumentReposit
/// <param name="authRepository">Injected repository that handles authentication.</param>
/// <param name="configuration">The injected configuration provider.</param>
/// <param name="jsonOptions">The jsonOptions.</param>
/// <param name="pollyPipelineProvider">The polly retry policy.</param>
public MayanDocumentRepository(
ILogger<MayanDocumentRepository> logger,
IHttpClientFactory httpClientFactory,
IEdmsAuthRepository authRepository,
IConfiguration configuration,
IOptions<JsonSerializerOptions> jsonOptions)
: base(logger, httpClientFactory, configuration, jsonOptions)
IOptions<JsonSerializerOptions> jsonOptions,
ResiliencePipelineProvider<string> pollyPipelineProvider)
: base(logger, httpClientFactory, configuration, jsonOptions, pollyPipelineProvider)
{
_authRepository = authRepository;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Pims.Api.Models.Mayan;
using Pims.Api.Models.Mayan.Metadata;
using Pims.Api.Models.Requests.Http;
using Polly.Registry;

namespace Pims.Api.Repositories.Mayan
{
Expand All @@ -30,13 +31,15 @@ public class MayanMetadataRepository : MayanBaseRepository, IEdmsMetadataReposit
/// <param name="authRepository">Injected repository that handles authentication.</param>
/// <param name="configuration">The injected configuration provider.</param>
/// <param name="jsonOptions">The json options.</param>
/// <param name="pollyPipelineProvider">The polly retry policy.</param>
public MayanMetadataRepository(
ILogger<MayanMetadataRepository> logger,
IHttpClientFactory httpClientFactory,
IEdmsAuthRepository authRepository,
IConfiguration configuration,
IOptions<JsonSerializerOptions> jsonOptions)
: base(logger, httpClientFactory, configuration, jsonOptions)
IOptions<JsonSerializerOptions> jsonOptions,
ResiliencePipelineProvider<string> pollyPipelineProvider)
: base(logger, httpClientFactory, configuration, jsonOptions, pollyPipelineProvider)
{
_authRepository = authRepository;
}
Expand Down
18 changes: 18 additions & 0 deletions source/backend/api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Security.Claims;
using System.Text;
Expand Down Expand Up @@ -44,6 +45,7 @@
using Pims.Core.Api.Exceptions;
using Pims.Core.Api.Helpers;
using Pims.Core.Api.Middleware;
using Pims.Core.Configuration;
using Pims.Core.Converters;
using Pims.Core.Http;
using Pims.Core.Json;
Expand All @@ -52,6 +54,7 @@
using Pims.Dal.Repositories;
using Pims.Geocoder;
using Pims.Ltsa;
using Polly;
using Prometheus;

namespace Pims.Api
Expand Down Expand Up @@ -100,6 +103,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddSerilogging(this.Configuration);
var jsonSerializerOptions = this.Configuration.GenerateJsonSerializerOptions();
var pimsOptions = this.Configuration.GeneratePimsOptions();

services.AddMapster(jsonSerializerOptions, pimsOptions, options =>
{
options.Default.IgnoreNonMapped(true);
Expand Down Expand Up @@ -233,6 +237,20 @@ public void ConfigureServices(IServiceCollection services)
x.MultipartBodyLengthLimit = maxFileSize; // In case of multipart
});

PollyOptions pollyOptions = new();
this.Configuration.GetSection("Polly").Bind(pollyOptions);

services.AddResiliencePipeline<string, HttpResponseMessage>(HttpRequestClient.NetworkPolicyName, (builder) =>
{
builder.AddRetry(new()
{
BackoffType = DelayBackoffType.Exponential,
Delay = TimeSpan.FromSeconds(pollyOptions.DelayInSeconds),
MaxRetryAttempts = pollyOptions.MaxRetries,
ShouldHandle = new PredicateBuilder<HttpResponseMessage>().Handle<HttpRequestException>().HandleResult(response => (int)response.StatusCode >= 500 && (int)response.StatusCode <= 599),
});
});

// Export metrics from all HTTP clients registered in services
services.UseHttpClientMetrics();

Expand Down
4 changes: 4 additions & 0 deletions source/backend/api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,9 @@
"CDogsHost": "[CDOGS_HOST]",
"ServiceClientId": "[CLIENT_ID]",
"ServiceClientSecret": "[CLIENT_SECRET]"
},
"Polly": {
"MaxRetries": 3,
"DelayInSeconds": 1
}
}
1 change: 1 addition & 0 deletions source/backend/core.api/Pims.Core.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="Microsoft.OpenApi" Version="1.6.22" />
<PackageReference Include="Polly.Extensions" Version="8.5.1" />
<PackageReference Include="Serilog" Version="4.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
using Pims.Api.Models;
using Pims.Api.Models.CodeTypes;
using Pims.Api.Models.Requests.Http;
using Pims.Core.Http;
using Polly;
using Polly.Registry;

namespace Pims.Core.Api.Repositories.Rest
{
Expand All @@ -25,20 +28,25 @@ public abstract class BaseRestRepository : IRestRespository
protected readonly IHttpClientFactory _httpClientFactory;
protected readonly ILogger _logger;
protected readonly IOptions<JsonSerializerOptions> _jsonOptions;
private readonly ResiliencePipeline<HttpResponseMessage> _resiliencePipeline;

/// <summary>
/// Initializes a new instance of the <see cref="BaseRestRepository"/> class.
/// </summary>
/// <param name="logger">Injected Logger Provider.</param>
/// <param name="httpClientFactory">Injected Httpclient factory.</param>
/// <param name="jsonOptions"></param>
/// <param name="pollyPipelineProvider"></param>
protected BaseRestRepository(
ILogger logger,
IHttpClientFactory httpClientFactory,
IOptions<JsonSerializerOptions> jsonOptions)
IOptions<JsonSerializerOptions> jsonOptions,
ResiliencePipelineProvider<string> pollyPipelineProvider)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_jsonOptions = jsonOptions;
_resiliencePipeline = pollyPipelineProvider.GetPipeline<HttpResponseMessage>(HttpRequestClient.NetworkPolicyName);
}

public abstract void AddAuthentication(HttpClient client, string authenticationToken = null);
Expand All @@ -51,7 +59,7 @@ public async Task<ExternalResponse<T>> GetAsync<T>(Uri endpoint, string authenti
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
try
{
HttpResponseMessage response = await client.GetAsync(endpoint).ConfigureAwait(true);
HttpResponseMessage response = await _resiliencePipeline.ExecuteAsync(async cancellation => await client.GetAsync(endpoint, cancellation).ConfigureAwait(true));
var result = await ProcessResponse<T>(response);
return result;
}
Expand All @@ -74,7 +82,7 @@ public async Task<HttpResponseMessage> GetRawAsync(Uri endpoint, string authenti
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
try
{
HttpResponseMessage response = await client.GetAsync(endpoint).ConfigureAwait(true);
HttpResponseMessage response = await _resiliencePipeline.ExecuteAsync(async cancellation => await client.GetAsync(endpoint, cancellation).ConfigureAwait(true));
return response;
}
catch (Exception e)
Expand All @@ -96,7 +104,7 @@ public async Task<ExternalResponse<T>> PostAsync<T>(Uri endpoint, HttpContent co

try
{
HttpResponseMessage response = await client.PostAsync(endpoint, content).ConfigureAwait(true);
HttpResponseMessage response = await _resiliencePipeline.ExecuteAsync(async cancellation => await client.PostAsync(endpoint, content, cancellation).ConfigureAwait(true));
var result = await ProcessResponse<T>(response);
return result;
}
Expand All @@ -120,7 +128,7 @@ public async Task<ExternalResponse<T>> PutAsync<T>(Uri endpoint, HttpContent con

try
{
HttpResponseMessage response = await client.PutAsync(endpoint, content).ConfigureAwait(true);
HttpResponseMessage response = await _resiliencePipeline.ExecuteAsync(async cancellation => await client.PutAsync(endpoint, content, cancellation).ConfigureAwait(true));
var result = await ProcessResponse<T>(response);
return result;
}
Expand Down Expand Up @@ -150,7 +158,7 @@ public async Task<ExternalResponse<string>> DeleteAsync(Uri endpoint, string aut

try
{
HttpResponseMessage response = await client.DeleteAsync(endpoint).ConfigureAwait(true);
HttpResponseMessage response = await _resiliencePipeline.ExecuteAsync(async cancellation => await client.DeleteAsync(endpoint, cancellation).ConfigureAwait(true));

_logger.LogTrace("Response: {response}", response);

Expand Down
Loading
Loading