using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Web; using System.Text.Json; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Configuration; using System.Reflection.Metadata.Ecma335; using Shared.Models; using System.Net; using Microsoft.Extensions.Options; namespace Client; public class Client { public string baseUri; public string apiKey; public string searchdomain; public Client(string baseUri, string apiKey = "", string searchdomain = "") { this.baseUri = baseUri; this.apiKey = apiKey; this.searchdomain = searchdomain; } public Client(IOptions configuration) { string baseUri = configuration.Value.BaseUri; string? apiKey = configuration.Value.ApiKey; string? searchdomain = configuration.Value.Searchdomain; this.baseUri = baseUri; this.apiKey = apiKey ?? ""; this.searchdomain = searchdomain ?? ""; } public async Task EntityListAsync(bool returnEmbeddings = false) { return await EntityListAsync(searchdomain, returnEmbeddings); } public async Task EntityListAsync(string searchdomain, bool returnEmbeddings = false) { var url = $"{baseUri}/Entities?searchdomain={HttpUtility.UrlEncode(searchdomain)}&returnEmbeddings={HttpUtility.UrlEncode(returnEmbeddings.ToString())}"; return await FetchUrlAndProcessJson(HttpMethod.Get, url); } public async Task EntityIndexAsync(List jsonEntity) { return await EntityIndexAsync(JsonSerializer.Serialize(jsonEntity)); } public async Task EntityIndexAsync(string jsonEntity) { var content = new StringContent(jsonEntity, Encoding.UTF8, "application/json"); return await FetchUrlAndProcessJson(HttpMethod.Put, GetUrl($"{baseUri}", "Entities", []), content); } public async Task EntityDeleteAsync(string entityName) { return await EntityDeleteAsync(searchdomain, entityName); } public async Task EntityDeleteAsync(string searchdomain, string entityName) { var url = $"{baseUri}/Entity?apiKey={HttpUtility.UrlEncode(apiKey)}&searchdomain={HttpUtility.UrlEncode(searchdomain)}&entity={HttpUtility.UrlEncode(entityName)}"; return await FetchUrlAndProcessJson(HttpMethod.Delete, url); } public async Task SearchdomainListAsync() { return await FetchUrlAndProcessJson(HttpMethod.Get, GetUrl($"{baseUri}", "Searchdomains", [])); } public async Task SearchdomainCreateAsync() { return await SearchdomainCreateAsync(searchdomain); } public async Task SearchdomainCreateAsync(string searchdomain, SearchdomainSettings searchdomainSettings = new()) { return await FetchUrlAndProcessJson(HttpMethod.Post, GetUrl($"{baseUri}", "Searchdomain", new Dictionary() { {"searchdomain", searchdomain} }), new StringContent(JsonSerializer.Serialize(searchdomainSettings), Encoding.UTF8, "application/json")); } public async Task SearchdomainDeleteAsync() { return await SearchdomainDeleteAsync(searchdomain); } public async Task SearchdomainDeleteAsync(string searchdomain) { return await FetchUrlAndProcessJson(HttpMethod.Delete, GetUrl($"{baseUri}", "Searchdomain", new Dictionary() { {"searchdomain", searchdomain} })); } public async Task SearchdomainUpdateAsync(string newName, string settings = "{}") { SearchdomainUpdateResults updateResults = await SearchdomainUpdateAsync(searchdomain, newName, settings); searchdomain = newName; return updateResults; } public async Task SearchdomainUpdateAsync(string searchdomain, string newName, SearchdomainSettings settings = new()) { return await SearchdomainUpdateAsync(searchdomain, newName, JsonSerializer.Serialize(settings)); } public async Task SearchdomainUpdateAsync(string searchdomain, string newName, string settings = "{}") { return await FetchUrlAndProcessJson(HttpMethod.Put, GetUrl($"{baseUri}", "Searchdomain", new Dictionary() { {"searchdomain", searchdomain}, {"newName", newName} }), new StringContent(settings, Encoding.UTF8, "application/json")); } public async Task SearchdomainGetQueriesAsync(string searchdomain) { Dictionary parameters = new() { {"searchdomain", searchdomain} }; return await FetchUrlAndProcessJson(HttpMethod.Get, GetUrl($"{baseUri}/Searchdomain", "Queries", parameters)); } public async Task SearchdomainQueryAsync(string query) { return await SearchdomainQueryAsync(searchdomain, query); } public async Task SearchdomainQueryAsync(string searchdomain, string query, int? topN = null, bool returnAttributes = false) { Dictionary parameters = new() { {"searchdomain", searchdomain}, {"query", query} }; if (topN is not null) parameters.Add("topN", ((int)topN).ToString()); if (returnAttributes) parameters.Add("returnAttributes", returnAttributes.ToString()); return await FetchUrlAndProcessJson(HttpMethod.Post, GetUrl($"{baseUri}/Searchdomain", "Query", parameters), null); } public async Task SearchdomainDeleteQueryAsync(string searchdomain, string query) { Dictionary parameters = new() { {"searchdomain", searchdomain}, {"query", query} }; return await FetchUrlAndProcessJson(HttpMethod.Delete, GetUrl($"{baseUri}/Searchdomain", "Query", parameters)); } public async Task SearchdomainUpdateQueryAsync(string searchdomain, string query, List results) { Dictionary parameters = new() { {"searchdomain", searchdomain}, {"query", query} }; return await FetchUrlAndProcessJson( HttpMethod.Patch, GetUrl($"{baseUri}/Searchdomain", "Query", parameters), new StringContent(JsonSerializer.Serialize(results), Encoding.UTF8, "application/json")); } public async Task SearchdomainGetSettingsAsync(string searchdomain) { Dictionary parameters = new() { {"searchdomain", searchdomain} }; return await FetchUrlAndProcessJson(HttpMethod.Get, GetUrl($"{baseUri}/Searchdomain", "Settings", parameters)); } public async Task SearchdomainUpdateSettingsAsync(string searchdomain, SearchdomainSettings searchdomainSettings) { Dictionary parameters = new() { {"searchdomain", searchdomain} }; StringContent content = new(JsonSerializer.Serialize(searchdomainSettings), Encoding.UTF8, "application/json"); return await FetchUrlAndProcessJson(HttpMethod.Put, GetUrl($"{baseUri}/Searchdomain", "Settings", parameters), content); } public async Task SearchdomainGetQueryCacheSizeAsync(string searchdomain) { Dictionary parameters = new() { {"searchdomain", searchdomain} }; return await FetchUrlAndProcessJson(HttpMethod.Get, GetUrl($"{baseUri}/Searchdomain/QueryCache", "Size", parameters)); } public async Task SearchdomainClearQueryCache(string searchdomain) { Dictionary parameters = new() { {"searchdomain", searchdomain} }; return await FetchUrlAndProcessJson(HttpMethod.Post, GetUrl($"{baseUri}/Searchdomain/QueryCache", "Clear", parameters), null); } public async Task SearchdomainGetDatabaseSizeAsync(string searchdomain) { Dictionary parameters = new() { {"searchdomain", searchdomain} }; return await FetchUrlAndProcessJson(HttpMethod.Get, GetUrl($"{baseUri}/Searchdomain/Database", "Size", parameters)); } public async Task ServerGetModelsAsync() { return await FetchUrlAndProcessJson(HttpMethod.Get, GetUrl($"{baseUri}/Server", "Models", [])); } public async Task ServerGetEmbeddingCacheSizeAsync() { return await FetchUrlAndProcessJson(HttpMethod.Get, GetUrl($"{baseUri}/Server/EmbeddingCache", "Size", [])); } private async Task FetchUrlAndProcessJson(HttpMethod httpMethod, string url, HttpContent? content = null) { HttpRequestMessage requestMessage = new(httpMethod, url) { Content = content, }; requestMessage.Headers.Add("X-API-KEY", apiKey); using var client = new HttpClient(); var response = await client.SendAsync(requestMessage); string responseContent = await response.Content.ReadAsStringAsync(); if (response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.Unauthorized) throw new UnauthorizedAccessException(responseContent); // TODO implement distinct exceptions if (response.StatusCode == HttpStatusCode.InternalServerError) throw new Exception($"Request was unsuccessful due to an internal server error: {responseContent}"); // TODO implement proper InternalServerErrorException var result = JsonSerializer.Deserialize(responseContent) ?? throw new Exception($"Failed to deserialize JSON to type {typeof(T).Name}"); return result; } public static string GetUrl(string baseUri, string endpoint, Dictionary parameters) { var uriBuilder = new UriBuilder($"{baseUri}/{endpoint}"); var query = HttpUtility.ParseQueryString(uriBuilder.Query); foreach (var param in parameters) { query[param.Key] = param.Value; } uriBuilder.Query = query.ToString() ?? ""; return uriBuilder.Uri.ToString(); } }