Files
AdPlatform-Server/Management/Services/RegistrationClient.cs
2026-03-14 13:50:09 -07:00

151 lines
4.9 KiB
C#

using System.Text.Json;
namespace Management.Services;
/// <summary>
/// HTTP client for calling the Registration Azure Function.
///
/// Configuration (appsettings.json):
/// "Registration": {
/// "BaseUrl": "https://your-function-app.azurewebsites.net/api",
/// "FunctionKey": "your-function-key-here"
/// }
///
/// Registered in DI as a typed HttpClient.
/// </summary>
public class RegistrationClient
{
private readonly HttpClient _http;
private readonly ILogger<RegistrationClient> _log;
private static readonly JsonSerializerOptions JsonOpts = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true
};
public RegistrationClient(HttpClient http, IConfiguration config, ILogger<RegistrationClient> log)
{
_http = http;
_log = log;
var baseUrl = config["Registration:BaseUrl"];
var functionKey = config["Registration:FunctionKey"];
if (!string.IsNullOrWhiteSpace(baseUrl))
{
try
{
_http.BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/");
}
catch (UriFormatException ex)
{
log.LogWarning(ex, "[RegistrationClient] Invalid BaseUrl: {BaseUrl}", baseUrl);
}
}
else
{
log.LogWarning("[RegistrationClient] Registration:BaseUrl not configured — registration proxy disabled");
}
// Function key sent as query param (Azure Functions default auth)
if (!string.IsNullOrWhiteSpace(functionKey))
{
// Store key for per-request query string injection
_functionKey = functionKey;
}
_log.LogInformation("[RegistrationClient] Configured. BaseUrl={BaseUrl} KeyPresent={HasKey}",
_http.BaseAddress, !string.IsNullOrWhiteSpace(functionKey));
}
private readonly string? _functionKey;
// ── API Methods ──
public async Task<JsonDocument?> GetPendingAsync(CancellationToken ct)
{
return await GetAsync("registration/pending", ct);
}
public async Task<JsonDocument?> GetByIdAsync(string registrationId, CancellationToken ct)
{
return await GetAsync($"registration/item/{registrationId}", ct);
}
public async Task<JsonDocument?> RejectAsync(string registrationId, string? reason, CancellationToken ct)
{
return await PostAsync($"registration/action/{registrationId}/reject", new { reason }, ct);
}
public async Task<JsonDocument?> CompleteAsync(string registrationId, string? platformClientId, CancellationToken ct)
{
return await PostAsync($"registration/action/{registrationId}/complete", new { platformClientId }, ct);
}
// ── Internal HTTP helpers ──
private async Task<JsonDocument?> GetAsync(string path, CancellationToken ct)
{
try
{
var url = AppendKey(path);
_log.LogInformation("[RegistrationClient] GET {Path}", path);
var response = await _http.GetAsync(url, ct);
var body = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
{
_log.LogWarning("[RegistrationClient] GET {Path} → {Status}: {Body}",
path, (int)response.StatusCode, body[..Math.Min(200, body.Length)]);
return null;
}
return JsonDocument.Parse(body);
}
catch (Exception ex)
{
_log.LogError(ex, "[RegistrationClient] GET {Path} failed", path);
return null;
}
}
private async Task<JsonDocument?> PostAsync(string path, object? payload, CancellationToken ct)
{
try
{
var url = AppendKey(path);
var json = payload != null ? JsonSerializer.Serialize(payload, JsonOpts) : "{}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
_log.LogInformation("[RegistrationClient] POST {Path}", path);
var response = await _http.PostAsync(url, content, ct);
var body = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
{
_log.LogWarning("[RegistrationClient] POST {Path} → {Status}: {Body}",
path, (int)response.StatusCode, body[..Math.Min(200, body.Length)]);
}
return JsonDocument.Parse(body);
}
catch (Exception ex)
{
_log.LogError(ex, "[RegistrationClient] POST {Path} failed", path);
return null;
}
}
private string AppendKey(string path)
{
if (string.IsNullOrWhiteSpace(_functionKey))
return path;
var separator = path.Contains('?') ? '&' : '?';
return $"{path}{separator}code={_functionKey}";
}
}