Implements an ApiClient with basic response types and endpoint classes
This commit is contained in:
parent
0561f51467
commit
e5680743e6
|
@ -1,14 +1,16 @@
|
||||||
namespace JpnCardsPokemonSdk.Api;
|
using JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
public class Card
|
namespace JpnCardsPokemonSdk.Api;
|
||||||
|
|
||||||
|
public class Card : EndpointObject
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
public Set? Set { get; set; }
|
public Set? Set { get; set; }
|
||||||
|
|
||||||
public string[] Types { get; set; }
|
public string[]? Types { get; set; }
|
||||||
|
|
||||||
public int Hp { get; set; } = -1;
|
public int Hp { get; set; } = -1;
|
||||||
|
|
||||||
|
@ -32,11 +34,11 @@ public class Card
|
||||||
|
|
||||||
public int? ConvertedRetreadCost { get; set; }
|
public int? ConvertedRetreadCost { get; set; }
|
||||||
|
|
||||||
public string Supertype { get; set; }
|
public string? Supertype { get; set; }
|
||||||
|
|
||||||
public string[]? Subtypes { get; set; }
|
public string[]? Subtypes { get; set; }
|
||||||
|
|
||||||
public string Rarity { get; set; }
|
public string? Rarity { get; set; }
|
||||||
|
|
||||||
// TODO: Type of property is not documented. Has to be evaluated at a later time.
|
// TODO: Type of property is not documented. Has to be evaluated at a later time.
|
||||||
// public Legality[]? Legalities { get; set; }
|
// public Legality[]? Legalities { get; set; }
|
||||||
|
@ -47,7 +49,7 @@ public class Card
|
||||||
|
|
||||||
public int Number { get; set; }
|
public int Number { get; set; }
|
||||||
|
|
||||||
public string PrintedNumber { get; set; }
|
public string? PrintedNumber { get; set; }
|
||||||
|
|
||||||
public int Uuid { get; set; }
|
public int Uuid { get; set; }
|
||||||
}
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
namespace JpnCardsPokemonSdk.Api;
|
using JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
public class Set
|
namespace JpnCardsPokemonSdk.Api;
|
||||||
|
|
||||||
|
public class Set : EndpointObject
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
@ -10,7 +12,7 @@ public class Set
|
||||||
|
|
||||||
public string? ImageUrl { get; set; }
|
public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
public string Language { get; set; }
|
public string? Language { get; set; }
|
||||||
|
|
||||||
public int Year { get; set; }
|
public int Year { get; set; }
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ public class Set
|
||||||
|
|
||||||
public int PrintedCardCount { get; set; }
|
public int PrintedCardCount { get; set; }
|
||||||
|
|
||||||
public string SetCode { get; set; }
|
public string? SetCode { get; set; }
|
||||||
|
|
||||||
public int Uuid { get; set; }
|
public int Uuid { get; set; }
|
||||||
}
|
}
|
80
src/Client/ApiClient.cs
Normal file
80
src/Client/ApiClient.cs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
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 JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
using JpnCardsPokemonSdk.Client.Responses;
|
||||||
|
|
||||||
|
namespace JpnCardsPokemonSdk.Client;
|
||||||
|
|
||||||
|
public class ApiClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient _client;
|
||||||
|
|
||||||
|
#if NETCOREAPP3_1_OR_GREATER
|
||||||
|
public ApiClient(SocketsHttpHandler handler) : this(new HttpClient(handler))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public ApiClient() : this(new HttpClient())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TResponseType?> FetchDataAsync<TResponseType, TResponseGeneric>(string requestUri)
|
||||||
|
where TResponseType : IApiResponse<TResponseGeneric>, new()
|
||||||
|
{
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
IncludeFields = true,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await _client.GetFromJsonAsync<TResponseType>(requestUri, options);
|
||||||
|
|
||||||
|
if (response is IPageableApiResponse<TResponseType, TResponseGeneric> pageAbleApiResponse)
|
||||||
|
{
|
||||||
|
pageAbleApiResponse.CurrentApiClient = this;
|
||||||
|
pageAbleApiResponse.RememberRequestUri(requestUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EnumerableApiResponse<T>?> FetchDataAsync<T>(string? query = null, int page = 1)
|
||||||
|
where T : EndpointObject
|
||||||
|
{
|
||||||
|
var endpoint = EndpointFactory.GetApiEndpoint<T>();
|
||||||
|
|
||||||
|
return await FetchDataAsync<EnumerableApiResponse<T>, IEnumerable<T>>($"{endpoint.ApiUri()}?page={page}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SingleApiResponse<T>?> FetchByIdAsync<T>(int id) where T : EndpointObject
|
||||||
|
{
|
||||||
|
var endpoint = EndpointFactory.GetApiEndpoint<T>();
|
||||||
|
|
||||||
|
return await FetchDataAsync<SingleApiResponse<T>, T>($"{endpoint.ApiUri()}/id={id}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SingleApiResponse<T>?> FetchByUuigAsync<T>(int uuid) where T : EndpointObject
|
||||||
|
{
|
||||||
|
var endpoint = EndpointFactory.GetApiEndpoint<T>();
|
||||||
|
|
||||||
|
return await FetchDataAsync<SingleApiResponse<T>, T>($"{endpoint.ApiUri()}/uuid={uuid}");
|
||||||
|
}
|
||||||
|
}
|
9
src/Client/Endpoints/CardEndpoint.cs
Normal file
9
src/Client/Endpoints/CardEndpoint.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
|
internal class CardEndpoint : IApiEndpoint
|
||||||
|
{
|
||||||
|
string IApiEndpoint.ApiUri()
|
||||||
|
{
|
||||||
|
return "card";
|
||||||
|
}
|
||||||
|
}
|
36
src/Client/Endpoints/EndpointFactory.cs
Normal file
36
src/Client/Endpoints/EndpointFactory.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
|
internal static class EndpointFactory
|
||||||
|
{
|
||||||
|
static EndpointFactory()
|
||||||
|
{
|
||||||
|
var knownTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t =>
|
||||||
|
typeof(EndpointObject).IsAssignableFrom(t) &&
|
||||||
|
t != typeof(EndpointObject));
|
||||||
|
|
||||||
|
foreach (var knownType in knownTypes) RuntimeHelpers.RunClassConstructor(knownType.TypeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<Type, IApiEndpoint> EndpointMapping { get; } = new();
|
||||||
|
|
||||||
|
public static void RegisterTypeEndpoint<T>(IApiEndpoint endpoint) where T : EndpointObject
|
||||||
|
{
|
||||||
|
EndpointMapping.Add(typeof(T), endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IApiEndpoint GetApiEndpoint<T>() where T : EndpointObject
|
||||||
|
{
|
||||||
|
foreach (var endpointMappingKey in EndpointMapping.Keys.Where(endpointMappingKey =>
|
||||||
|
typeof(T) == endpointMappingKey))
|
||||||
|
return EndpointMapping[endpointMappingKey];
|
||||||
|
|
||||||
|
// Todo: Custom exception class
|
||||||
|
throw new Exception($"No endpoint had been found for ${typeof(T).FullName}");
|
||||||
|
}
|
||||||
|
}
|
5
src/Client/Endpoints/EndpointObject.cs
Normal file
5
src/Client/Endpoints/EndpointObject.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
|
public abstract class EndpointObject
|
||||||
|
{
|
||||||
|
}
|
6
src/Client/Endpoints/IApiEndpoint.cs
Normal file
6
src/Client/Endpoints/IApiEndpoint.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
|
public interface IApiEndpoint
|
||||||
|
{
|
||||||
|
string ApiUri();
|
||||||
|
}
|
9
src/Client/Endpoints/SetEndpoint.cs
Normal file
9
src/Client/Endpoints/SetEndpoint.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
|
internal class SetEndpoint : IApiEndpoint
|
||||||
|
{
|
||||||
|
string IApiEndpoint.ApiUri()
|
||||||
|
{
|
||||||
|
return "card";
|
||||||
|
}
|
||||||
|
}
|
51
src/Client/Responses/EnumerableApiResponse.cs
Normal file
51
src/Client/Responses/EnumerableApiResponse.cs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Responses;
|
||||||
|
|
||||||
|
public class EnumerableApiResponse<T> : IApiResponse<IEnumerable<T>>,
|
||||||
|
IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>> where T : EndpointObject
|
||||||
|
{
|
||||||
|
private string? RequestUri { get; set; }
|
||||||
|
|
||||||
|
public int TotalPages => (int)Math.Ceiling((decimal)(
|
||||||
|
(IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>)this).TotalCount / (
|
||||||
|
(IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>)this).PageSize);
|
||||||
|
|
||||||
|
IEnumerable<T>? IApiResponse<IEnumerable<T>>.Data { get; set; }
|
||||||
|
|
||||||
|
ApiClient? IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>.CurrentApiClient { get; set; }
|
||||||
|
|
||||||
|
int IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>.Page { get; set; }
|
||||||
|
|
||||||
|
int IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>.PageSize { get; set; }
|
||||||
|
|
||||||
|
int IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>.Count { get; set; }
|
||||||
|
|
||||||
|
int IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>.TotalCount { get; set; }
|
||||||
|
|
||||||
|
async Task<EnumerableApiResponse<T>> IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>.
|
||||||
|
FetchNextPageAsync()
|
||||||
|
{
|
||||||
|
return await ((IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>)this).FetchPageAsync((
|
||||||
|
(IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>)this).Page + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<EnumerableApiResponse<T>?> IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>.
|
||||||
|
FetchPageAsync(int page)
|
||||||
|
{
|
||||||
|
var requestUri = RequestUri + "&page=" + page;
|
||||||
|
|
||||||
|
return await ((IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>)this).CurrentApiClient
|
||||||
|
?.FetchDataAsync<EnumerableApiResponse<T>, IEnumerable<T>>(requestUri)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPageableApiResponse<EnumerableApiResponse<T>, IEnumerable<T>>.RememberRequestUri(string requestUri)
|
||||||
|
{
|
||||||
|
// Remember full Uri without page
|
||||||
|
RequestUri = Regex.Replace(requestUri, @"page=\d*&?", "");
|
||||||
|
}
|
||||||
|
}
|
6
src/Client/Responses/IApiResponse.cs
Normal file
6
src/Client/Responses/IApiResponse.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Responses;
|
||||||
|
|
||||||
|
public interface IApiResponse<T>
|
||||||
|
{
|
||||||
|
T? Data { get; set; }
|
||||||
|
}
|
23
src/Client/Responses/IPageableApiResponse.cs
Normal file
23
src/Client/Responses/IPageableApiResponse.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Responses;
|
||||||
|
|
||||||
|
public interface IPageableApiResponse<TResponseType, TResponseGeneric>
|
||||||
|
where TResponseType : IApiResponse<TResponseGeneric>
|
||||||
|
{
|
||||||
|
ApiClient? CurrentApiClient { get; set; }
|
||||||
|
|
||||||
|
int Page { get; set; }
|
||||||
|
|
||||||
|
int PageSize { get; set; }
|
||||||
|
|
||||||
|
int Count { get; set; }
|
||||||
|
|
||||||
|
int TotalCount { get; set; }
|
||||||
|
|
||||||
|
void RememberRequestUri(string requestUri);
|
||||||
|
|
||||||
|
Task<TResponseType> FetchNextPageAsync();
|
||||||
|
|
||||||
|
Task<TResponseType> FetchPageAsync(int page);
|
||||||
|
}
|
8
src/Client/Responses/SingleApiResponse.cs
Normal file
8
src/Client/Responses/SingleApiResponse.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
using JpnCardsPokemonSdk.Client.Endpoints;
|
||||||
|
|
||||||
|
namespace JpnCardsPokemonSdk.Client.Responses;
|
||||||
|
|
||||||
|
public class SingleApiResponse<T> : IApiResponse<T> where T : EndpointObject
|
||||||
|
{
|
||||||
|
T? IApiResponse<T>.Data { get; set; }
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net7.0;net6.0;net461;netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>net7.0;net6.0;net462;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
<DocumentationFile>..\docs\JpnCardsPokemonSdk.xml</DocumentationFile>
|
<DocumentationFile>..\docs\JpnCardsPokemonSdk.xml</DocumentationFile>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
@ -38,6 +38,8 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
|
<PackageReference Include="System.Net.Http.Json" Version="7.0.0" />
|
||||||
<PackageReference Include="Vsxmd" Version="1.4.5">
|
<PackageReference Include="Vsxmd" Version="1.4.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|
Loading…
Reference in a new issue