using System.Text.Json; namespace Management.Services; /// /// 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. /// public class RegistrationClient { private readonly HttpClient _http; private readonly ILogger _log; private static readonly JsonSerializerOptions JsonOpts = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true }; public RegistrationClient(HttpClient http, IConfiguration config, ILogger 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 GetPendingAsync(CancellationToken ct) { return await GetAsync("registration/pending", ct); } public async Task GetByIdAsync(string registrationId, CancellationToken ct) { return await GetAsync($"registration/item/{registrationId}", ct); } public async Task RejectAsync(string registrationId, string? reason, CancellationToken ct) { return await PostAsync($"registration/action/{registrationId}/reject", new { reason }, ct); } public async Task CompleteAsync(string registrationId, string? platformClientId, CancellationToken ct) { return await PostAsync($"registration/action/{registrationId}/complete", new { platformClientId }, ct); } // ── Internal HTTP helpers ── private async Task 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 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}"; } }