using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace Beam.Api; public class ApiCall(HttpClient client, string uri, HttpMethod method, KeyValuePair[] headers, object? requestData, object? body, params HashSet successCodes) { public HttpClient Client { get; } = client; public object? RequestData { get; } = requestData; public object? Body { get; } public string Uri { get; } = uri; public HttpMethod Method { get; } = method; public KeyValuePair[] Headers { get; private set; } = headers; public HashSet SuccessCodes { get; } = successCodes; private string? ContentType = "application/json"; public async Task GetResponse(ILogger? logger, (int @try, int max)? tries = null, bool readToBuffer = true, CancellationToken ct = default) { SanitizeHeaders(); var request = new HttpRequestMessage(Method, Uri); request.Content = body is null ? request.Content : body is string stringBody ? new StringContent(stringBody) : JsonContent.Create(body); if (request.Content is not null) request.Content.Headers.ContentType = ContentType is null ? null : new System.Net.Http.Headers.MediaTypeHeaderValue(ContentType); foreach (var header in Headers) request.Headers.Add(header.Key, header.Value); logger?.LogInformation("Fetching '{}' with method '{}', content-type '{}', and headers '{}'", Uri, Method, ContentType, JsonSerializer.Serialize(request.Headers.ToDictionary())); var response = await Client.SendAsync(request, ct); if (tries is not null && tries?.@try < tries?.max && !SuccessCodes.Contains(response.StatusCode)) { await Task.Delay((int)Math.Min(Math.Pow(2, tries.Value.@try), 60) * 1000, ct); return await GetResponse(logger, (tries.Value.@try + 1, tries.Value.max), readToBuffer, ct); } return await ApiResponse.CreateAsync(response, logger, RequestData, readToBuffer, ct); } private void SanitizeHeaders() { Dictionary headers = []; foreach(var kvp in Headers) { if (kvp.Value.Length == 0) continue; if (kvp.Key == "Content-Type") { ContentType = kvp.Value[0]; } else { headers[kvp.Key] = kvp.Value; } } Headers = headers.ToArray(); } public static async Task Get(HttpClient client, string url, ILoggerFactory factory) => await new ApiCall(client, url, HttpMethod.Get, [], null, null).GetResponse(factory.CreateLogger()); }