From c8f08aa40e1671c430a850757921156a964336a6 Mon Sep 17 00:00:00 2001 From: hamzaalqurneh Date: Wed, 25 Mar 2026 15:01:28 +0300 Subject: [PATCH] Introduced `NativeHttpReceiver` as a new implementation of `INativeInfolinkReceiver` --- .../HttpReceiver/HttpReceiverInput.cs | 30 +++ .../HttpReceiver/HttpReceiverModels.cs | 24 +++ .../HttpReceiver/NativeHttpReceiver.cs | 179 ++++++++++++++++++ .../ServiceCollectionExtensions.cs | 4 + 4 files changed, 237 insertions(+) create mode 100644 SW.Bitween.NativeAdapters/HttpReceiver/HttpReceiverInput.cs create mode 100644 SW.Bitween.NativeAdapters/HttpReceiver/HttpReceiverModels.cs create mode 100644 SW.Bitween.NativeAdapters/HttpReceiver/NativeHttpReceiver.cs diff --git a/SW.Bitween.NativeAdapters/HttpReceiver/HttpReceiverInput.cs b/SW.Bitween.NativeAdapters/HttpReceiver/HttpReceiverInput.cs new file mode 100644 index 00000000..6db6e403 --- /dev/null +++ b/SW.Bitween.NativeAdapters/HttpReceiver/HttpReceiverInput.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace SW.Bitween.NativeAdapters.HttpReceiver; + +public class HttpReceiverInput +{ + public string? AuthType { get; set; } + public string? ApiKey { get; set; } + public string? LoginUrl { get; set; } + public string? LoginUsername { get; set; } + public string? LoginPassword { get; set; } + + [Required] + public string Url { get; set; } = string.Empty; + + public string? Headers { get; set; } + public string? ClientId { get; set; } + public string? ClientSecret { get; set; } + + [DefaultValue("application/json")] + public string ContentType { get; set; } = "application/json"; + + [DefaultValue("get")] + public string Verb { get; set; } = "get"; + + public string? DefaultRequest { get; set; } + + public string? ArrayPath { get; set; } +} \ No newline at end of file diff --git a/SW.Bitween.NativeAdapters/HttpReceiver/HttpReceiverModels.cs b/SW.Bitween.NativeAdapters/HttpReceiver/HttpReceiverModels.cs new file mode 100644 index 00000000..7a68e0ba --- /dev/null +++ b/SW.Bitween.NativeAdapters/HttpReceiver/HttpReceiverModels.cs @@ -0,0 +1,24 @@ +namespace SW.Bitween.NativeAdapters.HttpReceiver; + +public class ReceiverUserLoginModel +{ + public string? UserName { get; set; } + public string? Password { get; set; } +} + +// public class OAuth2Response +// { +// public string access_token { get; set; } +// } +// public class UserLoginModel +// { +// public string UserName { get; set; } +// public string Password { get; set; } +// } +// +// +// public class LoginResponse +// { +// public string Jwt { get; set; } +// public string Refresh { get; set; } +// } \ No newline at end of file diff --git a/SW.Bitween.NativeAdapters/HttpReceiver/NativeHttpReceiver.cs b/SW.Bitween.NativeAdapters/HttpReceiver/NativeHttpReceiver.cs new file mode 100644 index 00000000..da500220 --- /dev/null +++ b/SW.Bitween.NativeAdapters/HttpReceiver/NativeHttpReceiver.cs @@ -0,0 +1,179 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SW.PrimitiveTypes; + +namespace SW.Bitween.NativeAdapters.HttpReceiver; + +public class NativeHttpReceiver(IDynamicHttpProxy httpProxy) : INativeInfolinkReceiver +{ + IDictionary elementDictionary = new Dictionary(); + + private HttpReceiverInput _options = new(); + private HttpMethod HttpMethodFromString(string method) + { + switch (method.ToLower()) + { + case "get": + return HttpMethod.Get; + case "delete": + return HttpMethod.Delete; + case "put": + return HttpMethod.Put; + default: + return HttpMethod.Post; + } + } + + public async Task Initialize() + { + var data = await Task.FromResult(new{ }); + } + + public async Task Finalize() + { + var data = await Task.FromResult(new{ }); + } + + public async Task> ListFiles() + { + HttpClient client = httpProxy.GetClient(_options.Url); + if (_options.AuthType == "ApiKey") + client.DefaultRequestHeaders.Add("ApiKey", _options.ApiKey); + else if (_options.AuthType == "Basic") + { + string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(_options.LoginUsername + ":" + _options.LoginPassword)); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials); + } + else if (_options.AuthType == "Bearer") + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _options.LoginPassword); + else if (_options.AuthType == "Login") + { + string loginJson = JsonConvert.SerializeObject(new ReceiverUserLoginModel() + { + UserName = _options.LoginUsername, + Password = _options.LoginPassword + }); + HttpResponseMessage loginResponse = await client.PostAsync(new Uri(_options.LoginUrl), + new StringContent(loginJson, Encoding.UTF8, "application/json")); + loginResponse.EnsureSuccessStatusCode(); + if (loginResponse.StatusCode != HttpStatusCode.OK) + throw new Exception(loginResponse.StatusCode.ToString()); + string rs = await loginResponse.Content.ReadAsStringAsync(); + LoginResponse rsDeserialized = JsonConvert.DeserializeObject(rs); + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", rsDeserialized.Jwt); + } + else if (_options.AuthType == "OAuth2") + { + var oathRequest = new HttpRequestMessage(HttpMethod.Post, _options.LoginUrl); + var oauthContentDictionary = new List>(); + oauthContentDictionary.Add(new KeyValuePair("client_id", _options.ClientId)); + oauthContentDictionary.Add(new KeyValuePair("client_secret", _options.ClientSecret)); + oauthContentDictionary.Add(new KeyValuePair("grant_type", "client_credentials")); + var oauthContent = new FormUrlEncodedContent(oauthContentDictionary); + oathRequest.Content = oauthContent; + var oauthResponse = await client.SendAsync(oathRequest); + var res = await oauthResponse.Content.ReadAsStringAsync(); + var resDeserialized = JsonConvert.DeserializeObject(res); + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", resDeserialized.access_token); + } + + HttpContent content = null; + if (!string.IsNullOrEmpty(_options.DefaultRequest ?? string.Empty)) + { + string requestBody = _options.DefaultRequest ?? string.Empty; + string str = _options.ContentType.ToLower(); + switch (str) + { + case "application/x-www-form-urlencoded": + content = new FormUrlEncodedContent( JsonConvert.DeserializeObject>(requestBody)); + break; + case "application/json": + content = new StringContent(requestBody, Encoding.UTF8, "application/json"); + break; + default: + content = new StringContent(requestBody, Encoding.UTF8, _options.ContentType); + break; + } + } + + Uri uri = new Uri(_options.Url); + HttpRequestMessage request = new HttpRequestMessage() + { + RequestUri = uri, + Method = HttpMethodFromString(_options.Verb), + Content = content + }; + string headers1 = _options.Headers; + IEnumerable> headers = headers1?.Split(',').Select((Func>) (h => + { + string[] strArray = h.Split(':'); + return new KeyValuePair(strArray[0], strArray[1]); + })); + if (headers != null) + { + foreach (KeyValuePair keyValuePair1 in headers) + { + KeyValuePair keyValuePair = keyValuePair1; + request.Headers.Add(keyValuePair.Key, keyValuePair.Value); + } + } + HttpResponseMessage response = await client.SendAsync(request); + + if (response.StatusCode < HttpStatusCode.OK || response.StatusCode >= HttpStatusCode.InternalServerError) + throw new Exception(response.StatusCode.ToString()); + string resp = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode >= HttpStatusCode.BadRequest) + throw new Exception($"Request failed with status {response.StatusCode}: {resp}"); + // XchangeFile file = response.StatusCode < HttpStatusCode.BadRequest ? new XchangeFile(resp) : new XchangeFile(resp, badData: true); + + var jsonResponse = JToken.Parse(resp); + + JArray items; + if (!string.IsNullOrEmpty(_options.ArrayPath)) + { + var token = jsonResponse.SelectToken(_options.ArrayPath); + if (token is JArray arr) + items = arr; + else + throw new Exception($"The path '{_options.ArrayPath}' did not resolve to any token in the response."); + } + else + { + items = jsonResponse is JArray rootArray + ? rootArray + : new JArray(jsonResponse); + } + + for (int i = 0; i < items.Count; i++) + { + elementDictionary.TryAdd(i.ToString(), items[i].ToString()); + } + + return elementDictionary.Keys; + } + + public async Task GetFile(string fileId) + { + return new XchangeFile(elementDictionary[fileId]); + } + + public async Task DeleteFile(string fileId) + { + var data = await Task.FromResult(new{ }); + } + + public string Name => "NativeHttpReceiver"; + + public void InitializeStartupValues(IDictionary settings) + { + _options = settings.ConvertTo(); + } + + public Type StartupValuesType => typeof(HttpReceiverInput); +} \ No newline at end of file diff --git a/SW.Bitween.NativeAdapters/ServiceCollectionExtensions.cs b/SW.Bitween.NativeAdapters/ServiceCollectionExtensions.cs index 3d224c54..7e85f400 100644 --- a/SW.Bitween.NativeAdapters/ServiceCollectionExtensions.cs +++ b/SW.Bitween.NativeAdapters/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using SW.Bitween.NativeAdapters.HttpReceiver; namespace SW.Bitween.NativeAdapters; @@ -23,5 +24,8 @@ public static void AddNativeAdapters(this IServiceCollection serviceCollection) serviceCollection.AddScoped(); serviceCollection.AddScoped(); + + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); } } \ No newline at end of file