Skip to content

Commit d42a964

Browse files
Implement GetAllErrorLog API call implemented in CentralizedLogging.Sdk
1 parent 98b4ecb commit d42a964

File tree

11 files changed

+147
-47
lines changed

11 files changed

+147
-47
lines changed
Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1-
using Microsoft.AspNetCore.Mvc;
1+
using CentralizedLogging.Sdk.Abstractions;
2+
using Microsoft.AspNetCore.Mvc;
23

34
namespace ApiIntegrationMvc.Areas.Admin.Controllers
45
{
56
[Area("Admin")]
67
public class LogsController : Controller
78
{
8-
public IActionResult Index()
9+
private readonly ICentralizedLoggingClient _centralizedlogs;
10+
private readonly IAccessTokenProvider _cache;
11+
private readonly IHttpContextAccessor _http;
12+
public LogsController(ICentralizedLoggingClient centralizedlogs, IAccessTokenProvider cache, IHttpContextAccessor http) => (_centralizedlogs, _cache, _http) = (centralizedlogs, cache, http);
13+
14+
public async Task<IActionResult> Index(CancellationToken ct)
915
{
10-
return RedirectToAction("Index", "Home", new { area = "Home" });
11-
//return View();
16+
string token = await _cache.GetAccessTokenAsync(ct);
17+
18+
var result = await _centralizedlogs.GetAllErrorAsync(ct);
19+
20+
21+
return View();
1222
}
1323
}
1424
}

ApiIntegrationMvc/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using Microsoft.AspNetCore.Authentication.Cookies;
2-
using UserManagement.Sdk;
3-
using UserManagement.Sdk.Abstractions;
42
using UserManagement.Sdk.Extensions;
3+
using CentralizedLogging.Sdk.Extensions;
54

65
var builder = WebApplication.CreateBuilder(args);
76

@@ -11,7 +10,7 @@
1110
builder.Services.AddHttpContextAccessor(); // required for the above
1211

1312
builder.Services.AddUserManagementSdk();
14-
13+
builder.Services.AddCentralizedLoggingSdk();
1514

1615

1716
// Add services to the container.
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
{
1+
{
22
"UserManagement": {
33
"BaseAddress": "https://localhost:7093/",
44
"Timeout": "00:00:30",
55
"EnableResiliencePolicies": true
6+
},
7+
"CentralizedLogging": {
8+
"BaseAddress": "https://localhost:7180/",
9+
"Timeout": "00:00:30",
10+
"EnableResiliencePolicies": true
611
}
712
}

ApiIntegrationMvc/appsettings.Production.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,10 @@
33
"BaseAddress": "http://localhost:7093/",
44
"Timeout": "00:00:30",
55
"EnableResiliencePolicies": true
6+
},
7+
"CentralizedLogging": {
8+
"BaseAddress": "https://localhost:7180/",
9+
"Timeout": "00:00:30",
10+
"EnableResiliencePolicies": true
611
}
712
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-

1+
using CentralizedLogging.Contracts.Models;
22
namespace CentralizedLogging.Sdk.Abstractions
33
{
44
public interface ICentralizedLoggingClient
55
{
6-
//Task<AuthResponse> LoginAsync(LoginRequest request, CancellationToken ct = default);
6+
Task<List<GetAllErrorsResponseModel>> GetAllErrorAsync(CancellationToken ct = default);
77
}
88
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Net.Http.Headers;
2+
using CentralizedLogging.Sdk.Abstractions;
3+
4+
namespace CentralizedLogging.Sdk.Auth
5+
{
6+
// IMPORTANT: register this as Transient in DI.
7+
// It asks the IAccessTokenProvider for the current token each request.
8+
internal sealed class BearerTokenHandler : DelegatingHandler
9+
{
10+
private readonly IAccessTokenProvider _tokenProvider;
11+
12+
public BearerTokenHandler(IAccessTokenProvider tokenProvider)
13+
=> _tokenProvider = tokenProvider;
14+
15+
protected override async Task<HttpResponseMessage> SendAsync(
16+
HttpRequestMessage request,
17+
CancellationToken cancellationToken)
18+
{
19+
var token = await _tokenProvider.GetAccessTokenAsync(cancellationToken).ConfigureAwait(false);
20+
21+
if (!string.IsNullOrWhiteSpace(token))
22+
{
23+
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
24+
}
25+
26+
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
27+
}
28+
}
29+
}

CentralizedLogging.Sdk/CentralizedLogging.Sdk.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" />
11+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />
12+
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.8" />
1013
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.8" />
1114
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.8" />
1215
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.8" />
@@ -17,4 +20,8 @@
1720
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0" />
1821
</ItemGroup>
1922

23+
<ItemGroup>
24+
<ProjectReference Include="..\CentralizedLogging.Contracts\CentralizedLogging.Contracts.csproj" />
25+
</ItemGroup>
26+
2027
</Project>
Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System.Net.Http.Json;
22
using System.Text.Json;
3+
using CentralizedLogging.Contracts.Models;
34
using CentralizedLogging.Sdk.Abstractions;
5+
using Microsoft.AspNetCore.Mvc;
46

57
namespace CentralizedLogging.Sdk
68
{
@@ -10,31 +12,10 @@ internal sealed class CentralizedLoggingClient : ICentralizedLoggingClient
1012

1113
public CentralizedLoggingClient(HttpClient http) => _http = http;
1214

13-
//public async Task<AuthResponse> LoginAsync(LoginRequest request, CancellationToken ct = default)
14-
//{
15-
// var resp = await _http.PostAsJsonAsync("api/users/authenticate", request, ct);
16-
// var contentType = resp.Content.Headers.ContentType?.MediaType ?? "";
17-
18-
// var body = await resp.Content.ReadAsStringAsync(ct);
19-
20-
// if (!resp.IsSuccessStatusCode)
21-
// {
22-
// // Try to parse ProblemDetails for a better message
23-
// ProblemDetails? prob = null;
24-
// if (contentType.Contains("json", StringComparison.OrdinalIgnoreCase))
25-
// {
26-
// try { prob = JsonSerializer.Deserialize<ProblemDetails>(body); } catch { /* ignore */ }
27-
// }
28-
// var msg = prob?.Detail ?? prob?.Title ?? $"HTTP {(int)resp.StatusCode} {resp.ReasonPhrase}";
29-
// throw new HttpRequestException(msg);
30-
// }
31-
32-
// // Success → parse AuthResponse
33-
// if (!contentType.Contains("json", StringComparison.OrdinalIgnoreCase))
34-
// throw new InvalidOperationException($"Expected JSON but got '{contentType}'. Body: {body[..Math.Min(120, body.Length)]}");
35-
36-
// var auth = JsonSerializer.Deserialize<AuthResponse>(body, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
37-
// return auth;
38-
//}
15+
public async Task<List<GetAllErrorsResponseModel>> GetAllErrorAsync(CancellationToken ct = default)
16+
{
17+
var resp = await _http.GetFromJsonAsync<List<GetAllErrorsResponseModel>>("api/errorlogs");
18+
return resp;
19+
}
3920
}
4021
}

CentralizedLogging.Sdk/Extensions/ServiceCollectionExtensions.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
using Microsoft.Extensions.Configuration;
1+
using CentralizedLogging.Sdk.Abstractions;
2+
using CentralizedLogging.Sdk.Auth;
3+
using CentralizedLogging.Sdk.Configuration;
4+
using Microsoft.Extensions.Configuration;
25
using Microsoft.Extensions.DependencyInjection;
36
using Microsoft.Extensions.Options;
47
using Polly;
5-
using CentralizedLogging.Sdk.Abstractions;
6-
using CentralizedLogging.Sdk.Configuration;
78

89

910
namespace CentralizedLogging.Sdk.Extensions
1011
{
1112
public static class ServiceCollectionExtensions
1213
{
13-
public static IServiceCollection AddUserManagementSdk(
14+
public static IServiceCollection AddCentralizedLoggingSdk(
1415
this IServiceCollection services,
1516
Action<CentralizedLoggingOptions>? configure = null)
1617
{
@@ -24,7 +25,14 @@ public static IServiceCollection AddUserManagementSdk(
2425
if (configure is not null)
2526
services.PostConfigure(configure);
2627

27-
28+
// Delegating handler MUST be transient
29+
services.AddTransient<BearerTokenHandler>();
30+
31+
// Register the token provider (memory-based)
32+
services.AddScoped<IAccessTokenProvider, MemoryCacheAccessTokenProvider>();
33+
34+
35+
2836
// The Typed client
2937
services.AddHttpClient<ICentralizedLoggingClient, CentralizedLoggingClient>((sp, http) =>
3038
{
@@ -34,7 +42,8 @@ public static IServiceCollection AddUserManagementSdk(
3442

3543
http.BaseAddress = opts.BaseAddress;
3644
http.Timeout = opts.Timeout;
37-
})
45+
})
46+
.AddHttpMessageHandler<BearerTokenHandler>()
3847
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
3948
{
4049
// If you need custom certs, proxies, cookies, etc.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.Extensions.Caching.Memory;
3+
using System.Security.Claims;
4+
using CentralizedLogging.Sdk.Abstractions;
5+
6+
namespace CentralizedLogging.Sdk
7+
{
8+
public sealed class MemoryCacheAccessTokenProvider : IAccessTokenProvider
9+
{
10+
private readonly IMemoryCache _cache;
11+
private readonly IHttpContextAccessor _http;
12+
13+
public MemoryCacheAccessTokenProvider(IMemoryCache cache, IHttpContextAccessor http)
14+
{
15+
_cache = cache;
16+
_http = http;
17+
}
18+
19+
public Task<string?> GetAccessTokenAsync(CancellationToken ct = default)
20+
{
21+
var user = _http.HttpContext?.User;
22+
var uid =
23+
user?.FindFirst("sub")?.Value
24+
?? user?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
25+
26+
if (string.IsNullOrEmpty(uid)) return Task.FromResult<string?>(null);
27+
28+
var key = $"token:{uid}";
29+
_cache.TryGetValue(key, out string? token);
30+
return Task.FromResult(token);
31+
}
32+
33+
// Optional helper method to set the token into cache
34+
public void SetAccessToken(string token, int userId, DateTime expiresAtUtc)
35+
{
36+
var ttl = ToTtl(expiresAtUtc);
37+
_cache.Set($"token:{userId}", token, ttl);
38+
}
39+
40+
public Task RemoveAsync(string userId, CancellationToken ct = default)
41+
{
42+
_cache.Remove($"token:{userId}");
43+
return Task.CompletedTask;
44+
}
45+
46+
public static TimeSpan ToTtl(DateTime expiresAtUtc, TimeSpan? safety = null)
47+
{
48+
// ensure it's treated as UTC
49+
if (expiresAtUtc.Kind != DateTimeKind.Utc)
50+
expiresAtUtc = DateTime.SpecifyKind(expiresAtUtc, DateTimeKind.Utc);
51+
52+
var ttl = expiresAtUtc - DateTime.UtcNow;
53+
54+
// subtract a small safety margin to avoid edge expiries
55+
ttl -= safety ?? TimeSpan.FromSeconds(15);
56+
57+
return ttl > TimeSpan.Zero ? ttl : TimeSpan.Zero;
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)