Skip to content

Commit

Permalink
clear cache in case of 401 response; test refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
tl-Roberto-Mancinelli committed Dec 16, 2024
1 parent 99b38cd commit 250d2ef
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 259 deletions.
16 changes: 14 additions & 2 deletions src/TrueLayer/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,18 @@ private static readonly string TlAgentHeader

private readonly HttpClient _httpClient;
private readonly TrueLayerOptions _options;
private readonly IAuthTokenCache _authTokenCache;

/// <summary>
/// Creates a new <see cref="ApiClient"/> instance with the provided configuration, HTTP client factory and serializer.
/// </summary>
/// <param name="httpClient">The client used to make HTTP requests.</param>
/// <param name="options"></param>
public ApiClient(HttpClient httpClient, IOptions<TrueLayerOptions> options)
public ApiClient(HttpClient httpClient, IOptions<TrueLayerOptions> options, IAuthTokenCache authTokenCache)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
_authTokenCache = authTokenCache;
}

/// <inheritdoc />
Expand Down Expand Up @@ -138,6 +140,11 @@ private async Task<ApiResponse<TData>> CreateResponseAsync<TData>(HttpResponseMe
httpResponse.Headers.TryGetValues(CustomHeaders.TraceId, out var traceIdHeader);
string? traceId = traceIdHeader?.FirstOrDefault();

if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
{
_authTokenCache.Clear();
}

if (httpResponse.IsSuccessStatusCode && httpResponse.StatusCode != HttpStatusCode.NoContent)
{
var data = await DeserializeJsonAsync<TData>(httpResponse, traceId, cancellationToken);
Expand All @@ -159,6 +166,11 @@ private async Task<ApiResponse> CreateResponseAsync(HttpResponseMessage httpResp
httpResponse.Headers.TryGetValues(CustomHeaders.TraceId, out var traceIdHeader);
string? traceId = traceIdHeader?.FirstOrDefault();

if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
{
_authTokenCache.Clear();
}

// In .NET Standard 2.1 HttpResponse.Content can be null
if (httpResponse.Content?.Headers.ContentType?.MediaType == "application/problem+json")
{
Expand Down Expand Up @@ -296,7 +308,7 @@ private Task<HttpResponseMessage> SendRequestAsync(
}
}

// HttpCompletionOption.ResponseHeadersRead reduces allocations by by avoiding the pre-buffering of the response content
// HttpCompletionOption.ResponseHeadersRead reduces allocations by avoiding the pre-buffering of the response content
// and allows us to access the content stream faster.
// Doing so requires that always dispose of HttpResponseMessage to free up the connection
// Ref: https://www.stevejgordon.co.uk/using-httpcompletionoption-responseheadersread-to-improve-httpclient-performance-dotnet
Expand Down
5 changes: 2 additions & 3 deletions src/TrueLayer/IAuthTokenCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ void Set(
TimeSpan absoluteExpirationRelativeToNow);

/// <summary>
/// Removes the GetAuthTokenResponse associated with the given key.
/// Removes all entries in the cache.
/// </summary>
/// <param name="key">A string identifying the entry.</param>
void Remove(string key);
void Clear();
}
7 changes: 5 additions & 2 deletions src/TrueLayer/InMemoryAuthTokenCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ public bool TryGetValue(string key, out ApiResponse<GetAuthTokenResponse>? value
public void Set(string key, ApiResponse<GetAuthTokenResponse> value, TimeSpan absoluteExpirationRelativeToNow)=>
_memoryCache.Set(key, value, absoluteExpirationRelativeToNow);

public void Remove(string key) =>
_memoryCache.Remove(key);
public void Clear()
{
var cache = _memoryCache as MemoryCache;
cache?.Clear();
}
}
}
6 changes: 4 additions & 2 deletions src/TrueLayer/Mandates/MandatesApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ public async Task<ApiResponse<CreateMandateResponse>> CreateMandate(CreateMandat
{
mandateRequest.NotNull(nameof(mandateRequest));
idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
var type = mandateRequest.Mandate.Match(t0 => t0.Type, t1 => t1.Type);
var authResponse = await _auth.GetAuthToken(new GetAuthTokenRequest($"recurring_payments:{type}"), cancellationToken);
var mandateType = mandateRequest.Mandate.Match(
vrpCommercial => vrpCommercial.Type,
vrpSweeping => vrpSweeping.Type);
var authResponse = await _auth.GetAuthToken(new GetAuthTokenRequest($"recurring_payments:{mandateType}"), cancellationToken);

if (!authResponse.IsSuccessful)
{
Expand Down
6 changes: 2 additions & 4 deletions src/TrueLayer/Mandates/Model/MandateType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ public enum MandateType

public static class MandateTypeExtensions
{
public static string AsString(this MandateType mandateType)
{
return mandateType switch
public static string AsString(this MandateType mandateType) =>
mandateType switch
{
MandateType.Sweeping => "sweeping",
MandateType.Commercial => "commercial",
_ => throw new ArgumentException($"Invalid mandate type {mandateType}")
};
}
}
2 changes: 1 addition & 1 deletion src/TrueLayer/NullMemoryCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public void Set(string key, ApiResponse<GetAuthTokenResponse> value, TimeSpan ab
{
}

public void Remove(string key)
public void Clear()
{
}
}
Expand Down
Loading

0 comments on commit 250d2ef

Please sign in to comment.