bit more work on api client

This commit is contained in:
Schedel Pascal
2024-07-31 11:25:19 +02:00
parent 38dc09ab12
commit db5191b0b8
18 changed files with 145 additions and 42 deletions

View File

@@ -0,0 +1,5 @@
namespace OwnChar.Api.Exceptions;
public class ApiException(string message) : Exception(message)
{
}

3
OwnChar/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("OwnChar.Server")]

View File

@@ -0,0 +1,8 @@
using Pilz.Cryptography;
namespace OwnChar.Base.Data;
public class OwnCharRequest
{
public SecureString? AuthSecret { get; set; } = null;
}

View File

@@ -0,0 +1,11 @@
using System.Text.Json.Serialization;
namespace OwnChar.Base.Data;
public class OwnCharResponse
{
[JsonIgnore]
public bool IsSuccess => ErrorCode != OwnCharResponseError.None;
public OwnCharResponseError ErrorCode { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace OwnChar.Base.Data;
public enum OwnCharResponseError
{
None,
Default,
NotAuthorized,
}

View File

@@ -1,8 +1,6 @@
using OwnChar.Client.Data.Model; namespace OwnChar.Base.Data.Requests;
namespace OwnChar.Base.Data.Requests; public class GroupMemberAddRequest : OwnCharRequest
public class GroupMemberAddRequest
{ {
public Dictionary<long, MemberLevel> Members { get; } = []; public Dictionary<long, MemberLevel> Members { get; } = [];
} }

View File

@@ -2,7 +2,7 @@
namespace OwnChar.Base.Data.Requests; namespace OwnChar.Base.Data.Requests;
public class GroupMemberRemoveRequest public class GroupMemberRemoveRequest : OwnCharRequest
{ {
public List<long> Members { get; } = []; public List<long> Members { get; } = [];
} }

View File

@@ -2,7 +2,7 @@
namespace OwnChar.Base.Data.Requests; namespace OwnChar.Base.Data.Requests;
public class LoginRequest(string username, SecureString password) public class LoginRequest(string username, SecureString password) : OwnCharRequest
{ {
public string Username { get; } = username; public string Username { get; } = username;
public SecureString Password { get; } = password; public SecureString Password { get; } = password;

View File

@@ -1,5 +1,5 @@
namespace OwnChar.Base.Data.Requests; namespace OwnChar.Base.Data.Requests;
public class LogoutRequest public class LogoutRequest : OwnCharRequest
{ {
} }

View File

@@ -2,7 +2,7 @@
namespace OwnChar.Base.Data.Requests; namespace OwnChar.Base.Data.Requests;
public class UpdateRequest(OwnCharObjectUpdate update) public class UpdateRequest(OwnCharObjectUpdate update) : OwnCharRequest
{ {
public OwnCharObjectUpdate Update { get; } = update; public OwnCharObjectUpdate Update { get; } = update;
} }

View File

@@ -2,9 +2,9 @@
namespace OwnChar.Base.Data.Responses; namespace OwnChar.Base.Data.Responses;
public class LoginResponse(UserAccount account, UserProfile profile, string secret) public class LoginResponse(UserAccount? account, UserProfile? profile, string? secret) : OwnCharResponse
{ {
public UserAccount Account { get; } = account; public UserAccount? Account { get; } = account;
public UserProfile Profile { get; } = profile; public UserProfile? Profile { get; } = profile;
public string? Secret { get; } = secret; public string? Secret { get; } = secret;
} }

View File

@@ -1,5 +1,5 @@
namespace OwnChar.Client.Data.Clients; namespace OwnChar.Client.Data.Clients;
public class CharactersApiClient public class CharactersApiClient(OwnCharApiClient client)
{ {
} }

View File

@@ -1,5 +1,5 @@
namespace OwnChar.Client.Data.Clients; namespace OwnChar.Client.Data.Clients;
public class GroupsApiClient public class GroupsApiClient(OwnCharApiClient client)
{ {
} }

View File

@@ -1,14 +1,30 @@
namespace OwnChar.Client.Data.Clients; using OwnChar.Base.Data;
using OwnChar.Base.Data.Requests;
using OwnChar.Base.Data.Responses;
using OwnChar.Client.Data.Model;
using Pilz.Cryptography;
public class LoginApiClient namespace OwnChar.Client.Data.Clients;
public class LoginApiClient(OwnCharApiClient client)
{ {
public string Login(string username, string password) public async Task<UserProfile?> Login(string username, SecureString password)
{ {
var result = await client.MakeRequest<LoginRequest, LoginResponse>("/auth/login", new(username, password));
if (!string.IsNullOrEmpty(result.Secret) && result.Profile != null && result.Account != null)
{
client.AuthSecret = result.Secret;
return result.Profile;
} }
public string Logout() return null;
{ }
public async Task<bool> Logout()
{
await client.MakeRequest<LogoutRequest, OwnCharResponse>("/auth/logout", new());
client.AuthSecret = null;
return true;
} }
} }

View File

@@ -1,5 +1,5 @@
namespace OwnChar.Client.Data.Clients; namespace OwnChar.Client.Data.Clients;
public class UsersApiClient public class UsersApiClient(OwnCharApiClient client)
{ {
} }

View File

@@ -1,6 +1,63 @@
namespace OwnChar.Client.Data; using OwnChar.Api.Exceptions;
using OwnChar.Base.Data;
using OwnChar.Client.Data.Clients;
using System.Net.Http.Json;
namespace OwnChar.Client.Data;
public class OwnCharApiClient public class OwnCharApiClient
{ {
private readonly HttpClient httpClient = new(); private readonly HttpClient httpClient = new();
private readonly Dictionary<Type, object> clients = [];
internal string? AuthSecret { get; set; } = null;
public bool IsLoggedIn => AuthSecret != null;
public LoginApiClient Auth { get; }
public UsersApiClient Users { get; }
public CharactersApiClient Characters { get; }
public GroupsApiClient Groups { get; }
public OwnCharApiClient()
{
Auth = GetClient<LoginApiClient>();
Users = GetClient<UsersApiClient>();
Characters = GetClient<CharactersApiClient>();
Groups = GetClient<GroupsApiClient>();
}
public T GetClient<T>() where T : class
{
var t = typeof(T);
if (clients.TryGetValue(t, out var client) && client is T clientT1)
return clientT1;
if (Activator.CreateInstance(t, this) is T clientT2)
{
clients.Add(t, clientT2);
return clientT2;
}
throw new Exception("Client could not be created!");
}
public async Task<TResponse> MakeRequest<TRequest, TResponse>(string requestUrl, TRequest request) where TRequest : OwnCharRequest where TResponse : OwnCharResponse
{
if (await TryMakeRequest<TRequest, TResponse>(requestUrl, request) is TResponse response)
return response;
throw new ApiException(string.Format("The api request to \"{0}\" failed!", requestUrl));
}
public async Task<TResponse?> TryMakeRequest<TRequest, TResponse>(string requestUrl, TRequest request) where TRequest : OwnCharRequest where TResponse : OwnCharResponse
{
request.AuthSecret = AuthSecret;
var res = await httpClient.PostAsJsonAsync(requestUrl, request);
if (res.IsSuccessStatusCode)
return await res.Content.ReadFromJsonAsync<TResponse>();
return null;
}
} }

View File

@@ -1,22 +1,21 @@
using OwnChar.Client.Managers; using OwnChar.Client.Data;
using OwnChar.Data; using OwnChar.Client.Data.Model;
using OwnChar.Model; using OwnChar.Client.Managers;
using Pilz.Cryptography; using Pilz.Cryptography;
namespace OwnChar.Client; namespace OwnChar.Client;
public interface IOwnCharManager public interface IOwnCharManager
{ {
IDataManager? DataManager { get; set; }
UserAccountBase? CurrentUser { get; }
bool IsLoggedIn { get; } bool IsLoggedIn { get; }
OwnCharApiClient? Api { get; }
IUserManager Users { get; } IUserManager Users { get; }
IGroupsManager Groups { get; } IGroupsManager Groups { get; }
ICharacterManager Characters { get; } ICharacterManager Characters { get; }
bool Login(IDataManager? proxy, string? username, SecureString? password); Task<UserProfile?> Login(string? username, SecureString? password);
bool Logout(); Task<bool> Logout();
public static IOwnCharManager CreateDefault() public static IOwnCharManager CreateDefault()
{ {

View File

@@ -1,8 +1,7 @@
using OwnChar.Api.Exceptions; using OwnChar.Api.Exceptions;
using OwnChar.Client.Data;
using OwnChar.Client.Data.Model;
using OwnChar.Client.Managers; using OwnChar.Client.Managers;
using OwnChar.Data;
using OwnChar.Manager.Modules;
using OwnChar.Model;
using Pilz.Cryptography; using Pilz.Cryptography;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@@ -11,11 +10,10 @@ namespace OwnChar.Client;
internal class OwnCharManager : IOwnCharManager internal class OwnCharManager : IOwnCharManager
{ {
// User // User
public bool IsLoggedIn => CurrentUser != null; public bool IsLoggedIn => Api is not null && Api.IsLoggedIn;
public UserAccountBase? CurrentUser { get; private set; }
// Data Provider // Data Provider
public IDataManager? DataManager { get; set; } public OwnCharApiClient? Api { get; private set; }
// Manager // Manager
public IUserManager Users { get; } public IUserManager Users { get; }
@@ -29,10 +27,10 @@ internal class OwnCharManager : IOwnCharManager
Characters = new CharacterManager(this); Characters = new CharacterManager(this);
} }
[MemberNotNull(nameof(CurrentUser), nameof(DataManager))] [MemberNotNull(nameof(Api))]
internal protected void CheckLogin() internal protected void CheckLogin()
{ {
if (!IsLoggedIn || DataManager == null) if (!IsLoggedIn)
throw new LoginException("You are already logged in!"); throw new LoginException("You are already logged in!");
} }
@@ -40,25 +38,25 @@ internal class OwnCharManager : IOwnCharManager
/// Tries to login on the given data provider. /// Tries to login on the given data provider.
/// </summary> /// </summary>
/// <returns>Returns <see cref="true"/> if the login was successfull and <see cref="false"/> if not.</returns> /// <returns>Returns <see cref="true"/> if the login was successfull and <see cref="false"/> if not.</returns>
public bool Login(IDataManager? proxy, string? username, SecureString? password) public Task<UserProfile?> Login(string? username, SecureString? password)
{ {
ArgumentNullException.ThrowIfNull(proxy, nameof(proxy)); ArgumentNullException.ThrowIfNull(Api, nameof(Api));
ArgumentException.ThrowIfNullOrWhiteSpace(username, nameof(username)); ArgumentException.ThrowIfNullOrWhiteSpace(username, nameof(username));
ArgumentException.ThrowIfNullOrWhiteSpace(password, nameof(password)); ArgumentException.ThrowIfNullOrWhiteSpace(password, nameof(password));
username = username.Trim().ToLower(); username = username.Trim().ToLower();
CurrentUser = proxy.Login(username, OwnCharUtils.HashPassword(username, password));
DataManager = proxy;
return IsLoggedIn; return Api.Auth.Login(username, OwnCharUtils.HashPassword(username, password));
} }
/// <summary> /// <summary>
/// Closes the session on the current data provider. /// Closes the session on the current data provider.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public bool Logout() public async Task<bool> Logout()
{ {
return DataManager?.Logout(CurrentUser) ?? true; if (Api is null)
return true;
return await Api.Auth.Logout();
} }
} }