using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using JpnCardsPokemon.Sdk.Api; using JpnCardsPokemon.Sdk.Utils.QueryFilter; namespace JpnCardsPokemon.Sdk.Client; /// /// A client to interact with the web api. /// public class ApiClient { private readonly HttpClient _client; #if NETCOREAPP3_1_OR_GREATER /// /// Creates a new instance of the ApiClient. /// /// Can pass a to use for the internal http client. public ApiClient(SocketsHttpHandler handler) : this(new HttpClient(handler)) { } #endif /// /// Creates a new instance of the ApiClient. /// public ApiClient() : this(new HttpClient()) { } /// /// Creates a new instance of the ApiClient. /// /// Can pass a http client to use. public ApiClient(HttpClient client) { _client = client; _client.BaseAddress = new Uri("https://www.jpn-cards.com/v2/"); _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue( new ProductHeaderValue("JpnCardsPokemonSdkCS", GetType().Assembly.GetName().Version?.ToString()))); } private async Task FetchInternalAsync(string requestUri) { var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, IncludeFields = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; return await _client.GetFromJsonAsync(requestUri, options); } private string SetQuery(string? filter) { return !string.IsNullOrEmpty(filter) ? $"set/{filter.TrimStart('/')}" : "set"; } /// /// Fetches all . /// /// Returns an enumerable containing all . public async Task> FetchSetsAsync() { return await FetchInternalAsync>(SetQuery(null)) ?? Enumerable.Empty(); } /// /// Fetches a by its id. /// /// Id of the to fetch. /// If existing returns the matching . public async Task FetchSetById(int id) { return await FetchInternalAsync(SetQuery(id.ToString())); } /// /// Fetches a by its uuid. /// /// Uuid of the to fetch. /// If existing returns the matching . public async Task FetchSetByUuid(int uuid) { return await FetchInternalAsync(SetQuery($"uuid/{uuid}")); } /// /// Fetches from a search query. /// /// The search query. /// Returns an enumerable of matching the . /// Thrown if the is empty. /// /// At least one filter query must be specified. More information about the query format can be found at /// https://jpn-cards-site.readthedocs.io/en/latest/api_docs/pokemon/v2/v2_api/#card-queries. /// public async Task> FetchCardsAsync(string query) { if (string.IsNullOrWhiteSpace(query)) throw new Exception("Query string required"); // JSON response is wrapped into a data property, so we parse as JsonDocument first before deserialization. var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, IncludeFields = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; var jsonData = await FetchInternalAsync($"card/{query}"); return jsonData?.RootElement.GetProperty("data").Deserialize>(options) ?? Enumerable.Empty(); } /// /// Fetches from a search query. /// /// Configured query builder to generate the search query. /// Returns an enumerable of matching the . /// At least one filter must be specified. public async Task?> FetchCardsAsync(IQueryFilterBuilder filterBuilder) { return await FetchCardsAsync(filterBuilder.BuildQueryString()); } }