using Castle.Core.Logging; using System.Web; namespace Pilz.Net.Api; public class ApiClient(string apiUrl) : IApiClient { protected readonly HttpClient httpClient = new(); protected readonly List clients = []; public virtual string ApiUrl { get; } = apiUrl; public string? AuthKey { get; set; } public virtual IApiMessageSerializer Serializer { get; set; } = new DefaultApiMessageSerializer(); public ILogger Log { get; set; } = NullLogger.Instance; public T GetClient() where T : IApiSubClient { foreach (var c in clients) { if (c is T t) return t; } if (T.CreateClient(this) is T client) { clients.Add(client); return client; } throw new ApiException("Could not create an instance of this client."); } public virtual async Task SendRequest(string route, HttpMethod method, ApiMessage? message, ApiParameterCollection? @params, IApiMessageSerializer? serializer) { serializer ??= Serializer; Log.InfoFormat("Send message to {0}", route); var res = await Send(route, method, message, @params, serializer); return new(res); } public virtual async Task> SendRequest(string route, HttpMethod method, ApiMessage? message, ApiParameterCollection? @params, IApiMessageSerializer? serializer) where TResponse : ApiMessage { serializer ??= Serializer; Log.InfoFormat("Send request to {0}", route); var res = await Send(route, method, message, @params, serializer); TResponse? result = null; if (res.IsSuccessStatusCode) result = serializer.Deserialize(await res.Content.ReadAsStringAsync(), typeof(TResponse)) as TResponse; return new(res, result); } protected virtual async Task Send(string route, HttpMethod method, ApiMessage? message, ApiParameterCollection? @params, IApiMessageSerializer serializer) { var url = ApiUrl + route + BuildParameters(@params); HttpContent content; Log.DebugFormat("Api endpoint url is {0}", url); Log.Debug("Create content"); if (message is not null) content = new StringContent(serializer.Serialize(message)!, null, "application/json"); else content = new StringContent(string.Empty, null, "application/json"); Log.Debug("Build headers"); content.Headers.Add("API-AUTH-KEY", EncodeAuthKey()); Log.Debug("Sending request"); var httpmsg = new HttpRequestMessage(method, url) { Content = content }; return await httpClient.SendAsync(httpmsg); } protected virtual string BuildParameters(ApiParameterCollection? @params) { if (@params == null || @params.Count == 0) return string.Empty; return "?" + string.Join("&", @params.Select(kvp => { var key = kvp.Key; var value = HttpUtility.UrlEncode(ConvertParameter(kvp.Value)); return $"{key}={value}"; })); } protected virtual string? ConvertParameter(object? @param) { return (string?)Convert.ChangeType(@param, typeof(string)); } protected virtual string? EncodeAuthKey() { return AuthKey; } }