Added XML comments

This commit is contained in:
Alexander Rufenach
2021-06-04 04:41:31 -04:00
parent 9f3eef2cce
commit 25d84797fc
8 changed files with 260 additions and 24 deletions
+74 -2
View File
@@ -12,15 +12,31 @@ using Jellyfin.Plugin.AnilistSync.API.Exceptions;
namespace Jellyfin.Plugin.AnilistSync.API
{
/// <summary>
/// Anilist API.
/// </summary>
public class AnilistApi
{
// Interfaces //
private readonly ILogger<AnilistApi> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly JsonSerializerOptions _jsonSerializerOptions;
// Base URLs //
/// <summary>
/// Base url for OAUth uses.
/// </summary>
public const string BaseOauthUrl = @"https://anilist.co/api/v2";
/// <summary>
/// Base url for GraphQL queries.
/// </summary>
public const string GraphQLUrl = @"https://graphql.anilist.co";
/// <summary>
/// Generic GraphQL query string used for list updates.
/// </summary>
public const string QueryString = @"
mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
SaveMediaListEntry(id: $id, mediaId: $mediaId, status: $status, progress: $progress ) {
@@ -30,10 +46,21 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
progress
}
}";
/// <summary>
/// Anilist client ID.
/// </summary>
public const int ClientId = 5659;
/// <summary>
/// Secret.
/// </summary>
public const string Secret = @"h7ym2GZ6OjrdJ9sygDP7kDnQWsBdTwp4U8s7pt4X";
/// <summary>
/// Initializes a new instance of <see cref="AnilistApi"/> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger{AnilistApi}"/> interface.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
public AnilistApi(ILogger<AnilistApi> logger, IHttpClientFactory httpClientFactory)
{
_logger = logger;
@@ -41,7 +68,11 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
_jsonSerializerOptions = JsonDefaults.GetOptions();
}
/// <summary>
/// Get token.
/// </summary>
/// <param name="code">code.</param>
/// <returns><see cref="CodeResponse"/></returns>
public async Task<CodeResponse?> GetToken(string? code)
{
string uri = @"/oauth/token";
@@ -58,6 +89,11 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
return await responseMessage.Content.ReadFromJsonAsync<CodeResponse>(_jsonSerializerOptions);
}
/// <summary>
/// Gets total episodes of specified Anilist mediaID.
/// </summary>
/// <param name="anilistId">Anilist ID.</param>
/// <returns><see cref="RootObject"/> containing total episodes parameter.</returns>
public async Task<RootObject?> GetEpisodes(int? anilistId)
{
GraphQLBody content = new GraphQLBody {
@@ -73,6 +109,11 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
return await Post(content);
}
/// <summary>
/// Gets the name and ID of the currently authenticated user.
/// </summary>
/// <param name="userToken">User token.</param>
/// <returns><see cref="RootObject"/>containing <see cref="User"/> object.</returns>
public async Task<RootObject?> GetUser(string? userToken)
{
GraphQLBody content = new GraphQLBody {
@@ -82,6 +123,12 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
return await PostWithAuth(userToken, content);
}
/// <summary>
/// Gets user's list ID of specified Anilist mediaID
/// </summary>
/// <param name="userToken">User token.</param>
/// <param name="anilistId">Anilist mediaID</param>
/// <returns><see cref="RootObject"/> containing <see cref="ListEntry"/></returns>
public async Task<RootObject?> GetListEntry(string? userToken, int? anilistId)
{
GraphQLBody content = new GraphQLBody
@@ -98,6 +145,13 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
return await PostWithAuth(userToken, content);
}
/// <summary>
/// Updates status of specified list item
/// </summary>
/// <param name="userToken">User token.</param>
/// <param name="listId">List ID</param>
/// <param name="status">Status.</param>
/// <returns><see cref="RootObject"/> containing updated list item.</returns>
public async Task<RootObject?> PostListStatusUpdate(string? userToken, int? listId, MediaListStatus? status)
{
GraphQLBody content = new GraphQLBody
@@ -114,6 +168,13 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
return await PostWithAuth(userToken, content);
}
/// <summary>
/// Updates progress of specified list item.
/// </summary>
/// <param name="userToken">User token.</param>
/// <param name="listId">List ID></param>
/// <param name="progress">Progress.</param>
/// <returns><see cref="RootObject"/> containing updated list item.</returns>
public async Task<RootObject?> PostListProgressUpdate(string? userToken, int? listId, int? progress)
{
GraphQLBody content = new GraphQLBody
@@ -130,6 +191,11 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
return await PostWithAuth(userToken, content);
}
/// <summary>
/// API private GraphQL Post WITHOUT authentication.
/// </summary>
/// <param name="graphQL"><see cref="GraphQLBody"/> containing query and variables</param>
/// <returns><see cref="RootObject"/> containing API response.</returns>
public async Task<RootObject?> Post(GraphQLBody graphQL)
{
var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).PostAsJsonAsync<GraphQLBody>(GraphQLUrl, graphQL, _jsonSerializerOptions);
@@ -144,6 +210,12 @@ mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
return root;
}
/// <summary>
/// API private GraphQL Post WITH authentication.
/// </summary>
/// <param name="userToken">User token.</param>
/// <param name="graphQL"><see cref="GraphQLBody"/> containing query and variables</param>
/// <returns><see cref="RootObject"/> containing API response.</returns>
public async Task<RootObject?> PostWithAuth(string? userToken, GraphQLBody graphQL)
{
using var requestMessage = new HttpRequestMessage
+123
View File
@@ -2,126 +2,249 @@
namespace Jellyfin.Plugin.AnilistSync.API
{
/// <summary>
/// Media list status enum,
/// </summary>
public enum MediaListStatus
{
/// <summary>Currently watching.</summary>
CURRENT,
/// <summary>Planning to watch.</summary>
PLANNING,
/// <summary>Completed.</summary>
COMPLETED,
/// <summary>Dropped.</summary>
DROPPED,
/// <summary>n hold.</summary>
PAUSED,
/// <summary>Rewatching.</summary>
REPEATING
}
/// <summary>
/// Root JSON object.
/// </summary>
public class RootObject
{
/// <summary>
/// Gets or sets data.
/// </summary>
[JsonPropertyName("data")]
public Data? Data { get; set; }
/// <summary>
/// Gets or sets errors.
/// </summary>
[JsonPropertyName("errors")]
public AnilistError[]? Errors { get; set; }
}
/// <summary>
/// Data JSON Object.
/// </summary>
public class Data
{
/// <summary>
/// Gets or sets user.
/// </summary>
[JsonPropertyName("Viewer")]
public User? User { get; set; }
/// <summary>
/// Gets or sets list entry.
/// </summary>
[JsonPropertyName("SaveMediaListEntry")]
public ListEntry? ListEntry { get; set; }
/// <summary>
/// Gets or sets media.
/// </summary>
[JsonPropertyName("Media")]
public Media? Media { get; set; }
}
/// <summary>
/// Error object.
/// </summary>
public class AnilistError
{
/// <summary>
/// Gets or sets error message.
/// </summary>
[JsonPropertyName("message")]
public string? ErrorMessage { get; set; }
/// <summary>
/// Gets or sets error status.
/// </summary>
[JsonPropertyName("status")]
public int? ErrorStatus { get; set; }
/// <summary>
/// Gets or sets error locations.
/// </summary>
[JsonPropertyName("locations")]
public Location[]? Locations { get; set; }
}
/// <summary>
/// Locations of error(s) object.
/// </summary>
public class Location
{
/// <summary>
/// Gets or sets line.
/// </summary>
[JsonPropertyName("line")]
public int? Line { get; set; }
/// <summary>
/// Gets or sets column.
/// </summary>
[JsonPropertyName("column")]
public int? Column { get; set; }
}
/// <summary>
/// List Entry object.
/// </summary>
public class ListEntry
{
/// <summary>
/// Gets or sets mediaId.
/// </summary>
[JsonPropertyName("mediaId")]
public int? MediaId { get; set; }
/// <summary>
/// Gets or sets ID.
/// </summary>
[JsonPropertyName("id")]
public int? Id { get; set; }
/// <summary>
/// Gets or sets progress.
/// </summary>
[JsonPropertyName("progress")]
public int? Progress { get; set; }
/// <summary>
/// Gets or sets status.
/// </summary>
[JsonPropertyName("status")]
public MediaListStatus? Status { get; set; }
}
/// <summary>
/// Media object.
/// </summary>
public class Media
{
/// <summary>
/// Gets or sets episodes.
/// </summary>
[JsonPropertyName("episodes")]
public int? Episodes { get; set; }
}
/// <summary>
/// User object.
/// </summary>
public class User
{
/// <summary>
/// Gets or sets user ID.
/// </summary>
[JsonPropertyName("id")]
public int? Id { get; set; }
/// <summary>
/// Gets or sets user name.
/// </summary>
[JsonPropertyName("name")]
public string? Name { get; set; }
}
/// <summary>
/// GraphQLBody object.
/// </summary>
public class GraphQLBody
{
/// <summary>
/// Gets or sets GraphQL query string.
/// </summary>
[JsonPropertyName("query")]
public string? Query { get; set; }
/// <summary>
/// Gets or sets GraphQL variables.
/// </summary>
[JsonPropertyName("variables")]
public ListEntry? Variables { get; set; }
}
/// <summary>
/// OAuth response object.
/// </summary>
public class OAuth
{
/// <summary>
/// Gets or sets grant type.
/// </summary>
[JsonPropertyName("grant_type")]
public string? GrantType { get; set; }
/// <summary>
/// Gets or sets client ID.
/// </summary>
[JsonPropertyName("client_id")]
public int? ClientId { get; set; }
/// <summary>
/// Gets or sets client secret.
/// </summary>
[JsonPropertyName("client_secret")]
public string? ClientSecret { get; set; }
/// <summary>
/// Gets or sets redirect URI.
/// </summary>
[JsonPropertyName("redirect_uri")]
public string? RedirectUri { get; set; }
/// <summary>
/// Gets or sets code.
/// </summary>
[JsonPropertyName("code")]
public string? Code { get; set; }
}
/// <summary>
/// Code response object.
/// </summary>
public class CodeResponse
{
/// <summary>
/// Gets or sets token type.
/// </summary>
[JsonPropertyName("token_type")]
public string? TokenType { get; set; }
/// <summary>
/// Gets or sets expires in.
/// </summary>
[JsonPropertyName("expires_in")]
public int? ExpiresIn { get; set; }
/// <summary>
/// Gets or sets access token.
/// </summary>
[JsonPropertyName("access_token")]
public string? AccessToken { get; set; }
/// <summary>
/// Gets or sets refresh token.
/// </summary>
[JsonPropertyName("refresh_token")]
public string? RefreshToken { get; set; }
}
@@ -5,6 +5,9 @@ using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Plugin.AnilistSync.API
{
/// <summary>
/// Anilist endpoints.
/// </summary>
[ApiController]
[Authorize(Policy = "DefaultAuthorization")]
[Route("AnilistSync")]
@@ -12,17 +15,31 @@ namespace Jellyfin.Plugin.AnilistSync.API
{
private readonly AnilistApi _anilistApi;
/// <summary>
/// Initializes a new instacne fo the <see cref="Endpoints"/> class.
/// </summary>
/// <param name="anilistApi"></param>
public Endpoints(AnilistApi anilistApi)
{
_anilistApi = anilistApi;
}
/// <summary>
/// Gets the OAuth token.
/// </summary>
/// <param name="userCode">User code.</param>
/// <returns>Code response containing token.</returns>
[HttpGet("oauth/token/{userCode}")]
public async Task<ActionResult<CodeResponse?>> GetToken([FromRoute] string userCode)
{
return await _anilistApi.GetToken(userCode);
}
/// <summary>
/// Gets the currently authenticated user
/// </summary>
/// <param name="userId">The user ID.</param>
/// <returns>Root object containing User object</returns>
[HttpGet("users/settings/{userId}")]
public async Task<ActionResult<RootObject?>> GetUser([FromRoute] Guid userId)
{