refactor: modularize Beam into new projects and interfaces
- Introduced modularity by splitting Beam into new projects: Beam.Abstractions, Beam.Models, and Beam.Downloaders. - Refactored existing classes into appropriate namespaces and projects. - Replaced specific implementations with abstractions (e.g., SourceLinkBuilder to LinkBuilder, State to IState, etc.). - Updated interfaces: added ITemplate, IArticleData, IDownloadReport, and others for improved extensibility. - Removed deprecated classes like SourceLinkBuilder and StateChangerFactory. - Enhanced link handling in downloaders by refactoring to use `string` over `SourceLink`. - Consolidated shared logic under Beam.Abstractions.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Beam {
|
||||
public class ApiCall(HttpClient client, string uri, HttpMethod method, KeyValuePair<string, string[]>[] headers, object? requestData, object? body, params HashSet<HttpStatusCode> 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<string, string[]>[] Headers { get; private set; } = headers;
|
||||
public HashSet<HttpStatusCode> SuccessCodes { get; } = successCodes;
|
||||
|
||||
private string? ContentType = "application/json";
|
||||
|
||||
public async Task<ApiResponse> GetResponse(ILogger<ApiResponse>? logger, (int @try, int max)? tries = null, 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), ct);
|
||||
}
|
||||
|
||||
return await ApiResponse.CreateAsync(response, logger, RequestData, ct);
|
||||
}
|
||||
|
||||
private void SanitizeHeaders() {
|
||||
Dictionary<string, string[]> 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<ApiResponse> Get(HttpClient client, string url, ILoggerFactory factory)
|
||||
=> await new ApiCall(client, url, HttpMethod.Get, [], null, null).GetResponse(factory.CreateLogger<ApiResponse>());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user