From db5191b0b809c350776886368f22ae60b11a346b Mon Sep 17 00:00:00 2001 From: Schedel Pascal Date: Wed, 31 Jul 2024 11:25:19 +0200 Subject: [PATCH] bit more work on api client --- OwnChar/Api/Exceptions/ApiException.cs | 5 ++ OwnChar/AssemblyInfo.cs | 3 + OwnChar/Base/Data/OwnCharRequest.cs | 8 +++ OwnChar/Base/Data/OwnCharResponse.cs | 11 ++++ OwnChar/Base/Data/OwnCharResponseError.cs | 8 +++ .../Data/Requests/GroupMemberAddRequest.cs | 6 +- .../Data/Requests/GroupMemberRemoveRequest.cs | 2 +- OwnChar/Base/Data/Requests/LoginRequest.cs | 2 +- OwnChar/Base/Data/Requests/LogoutRequest.cs | 2 +- OwnChar/Base/Data/Requests/UpdateRequest.cs | 2 +- OwnChar/Base/Data/Responses/LoginResponse.cs | 6 +- .../Data/Clients/CharactersApiClient.cs | 2 +- .../Client/Data/Clients/GroupsApiClient.cs | 2 +- OwnChar/Client/Data/Clients/LoginApiClient.cs | 26 ++++++-- OwnChar/Client/Data/Clients/UsersApiClient.cs | 2 +- OwnChar/Client/Data/OwnCharApiClient.cs | 59 ++++++++++++++++++- OwnChar/Client/IOwnCharManager.cs | 13 ++-- OwnChar/Client/OwnCharManager.cs | 28 ++++----- 18 files changed, 145 insertions(+), 42 deletions(-) create mode 100644 OwnChar/Api/Exceptions/ApiException.cs create mode 100644 OwnChar/AssemblyInfo.cs create mode 100644 OwnChar/Base/Data/OwnCharRequest.cs create mode 100644 OwnChar/Base/Data/OwnCharResponse.cs create mode 100644 OwnChar/Base/Data/OwnCharResponseError.cs diff --git a/OwnChar/Api/Exceptions/ApiException.cs b/OwnChar/Api/Exceptions/ApiException.cs new file mode 100644 index 0000000..58b64eb --- /dev/null +++ b/OwnChar/Api/Exceptions/ApiException.cs @@ -0,0 +1,5 @@ +namespace OwnChar.Api.Exceptions; + +public class ApiException(string message) : Exception(message) +{ +} diff --git a/OwnChar/AssemblyInfo.cs b/OwnChar/AssemblyInfo.cs new file mode 100644 index 0000000..ce0622b --- /dev/null +++ b/OwnChar/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("OwnChar.Server")] diff --git a/OwnChar/Base/Data/OwnCharRequest.cs b/OwnChar/Base/Data/OwnCharRequest.cs new file mode 100644 index 0000000..bff1c5c --- /dev/null +++ b/OwnChar/Base/Data/OwnCharRequest.cs @@ -0,0 +1,8 @@ +using Pilz.Cryptography; + +namespace OwnChar.Base.Data; + +public class OwnCharRequest +{ + public SecureString? AuthSecret { get; set; } = null; +} diff --git a/OwnChar/Base/Data/OwnCharResponse.cs b/OwnChar/Base/Data/OwnCharResponse.cs new file mode 100644 index 0000000..7de2ce7 --- /dev/null +++ b/OwnChar/Base/Data/OwnCharResponse.cs @@ -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; } +} diff --git a/OwnChar/Base/Data/OwnCharResponseError.cs b/OwnChar/Base/Data/OwnCharResponseError.cs new file mode 100644 index 0000000..7df389f --- /dev/null +++ b/OwnChar/Base/Data/OwnCharResponseError.cs @@ -0,0 +1,8 @@ +namespace OwnChar.Base.Data; + +public enum OwnCharResponseError +{ + None, + Default, + NotAuthorized, +} diff --git a/OwnChar/Base/Data/Requests/GroupMemberAddRequest.cs b/OwnChar/Base/Data/Requests/GroupMemberAddRequest.cs index adfa381..a2c0b77 100644 --- a/OwnChar/Base/Data/Requests/GroupMemberAddRequest.cs +++ b/OwnChar/Base/Data/Requests/GroupMemberAddRequest.cs @@ -1,8 +1,6 @@ -using OwnChar.Client.Data.Model; +namespace OwnChar.Base.Data.Requests; -namespace OwnChar.Base.Data.Requests; - -public class GroupMemberAddRequest +public class GroupMemberAddRequest : OwnCharRequest { public Dictionary Members { get; } = []; } diff --git a/OwnChar/Base/Data/Requests/GroupMemberRemoveRequest.cs b/OwnChar/Base/Data/Requests/GroupMemberRemoveRequest.cs index 17ee2f5..1d5c33a 100644 --- a/OwnChar/Base/Data/Requests/GroupMemberRemoveRequest.cs +++ b/OwnChar/Base/Data/Requests/GroupMemberRemoveRequest.cs @@ -2,7 +2,7 @@ namespace OwnChar.Base.Data.Requests; -public class GroupMemberRemoveRequest +public class GroupMemberRemoveRequest : OwnCharRequest { public List Members { get; } = []; } diff --git a/OwnChar/Base/Data/Requests/LoginRequest.cs b/OwnChar/Base/Data/Requests/LoginRequest.cs index d3b146d..43791e7 100644 --- a/OwnChar/Base/Data/Requests/LoginRequest.cs +++ b/OwnChar/Base/Data/Requests/LoginRequest.cs @@ -2,7 +2,7 @@ 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 SecureString Password { get; } = password; diff --git a/OwnChar/Base/Data/Requests/LogoutRequest.cs b/OwnChar/Base/Data/Requests/LogoutRequest.cs index 52d9c4d..07729e9 100644 --- a/OwnChar/Base/Data/Requests/LogoutRequest.cs +++ b/OwnChar/Base/Data/Requests/LogoutRequest.cs @@ -1,5 +1,5 @@ namespace OwnChar.Base.Data.Requests; -public class LogoutRequest +public class LogoutRequest : OwnCharRequest { } diff --git a/OwnChar/Base/Data/Requests/UpdateRequest.cs b/OwnChar/Base/Data/Requests/UpdateRequest.cs index bc0f086..022f513 100644 --- a/OwnChar/Base/Data/Requests/UpdateRequest.cs +++ b/OwnChar/Base/Data/Requests/UpdateRequest.cs @@ -2,7 +2,7 @@ namespace OwnChar.Base.Data.Requests; -public class UpdateRequest(OwnCharObjectUpdate update) +public class UpdateRequest(OwnCharObjectUpdate update) : OwnCharRequest { public OwnCharObjectUpdate Update { get; } = update; } diff --git a/OwnChar/Base/Data/Responses/LoginResponse.cs b/OwnChar/Base/Data/Responses/LoginResponse.cs index f24cc6d..9d7f8be 100644 --- a/OwnChar/Base/Data/Responses/LoginResponse.cs +++ b/OwnChar/Base/Data/Responses/LoginResponse.cs @@ -2,9 +2,9 @@ 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 UserProfile Profile { get; } = profile; + public UserAccount? Account { get; } = account; + public UserProfile? Profile { get; } = profile; public string? Secret { get; } = secret; } diff --git a/OwnChar/Client/Data/Clients/CharactersApiClient.cs b/OwnChar/Client/Data/Clients/CharactersApiClient.cs index 3b423e7..1127346 100644 --- a/OwnChar/Client/Data/Clients/CharactersApiClient.cs +++ b/OwnChar/Client/Data/Clients/CharactersApiClient.cs @@ -1,5 +1,5 @@ namespace OwnChar.Client.Data.Clients; -public class CharactersApiClient +public class CharactersApiClient(OwnCharApiClient client) { } diff --git a/OwnChar/Client/Data/Clients/GroupsApiClient.cs b/OwnChar/Client/Data/Clients/GroupsApiClient.cs index 355d903..a4e4244 100644 --- a/OwnChar/Client/Data/Clients/GroupsApiClient.cs +++ b/OwnChar/Client/Data/Clients/GroupsApiClient.cs @@ -1,5 +1,5 @@ namespace OwnChar.Client.Data.Clients; -public class GroupsApiClient +public class GroupsApiClient(OwnCharApiClient client) { } diff --git a/OwnChar/Client/Data/Clients/LoginApiClient.cs b/OwnChar/Client/Data/Clients/LoginApiClient.cs index 9fbdfe0..211a9bf 100644 --- a/OwnChar/Client/Data/Clients/LoginApiClient.cs +++ b/OwnChar/Client/Data/Clients/LoginApiClient.cs @@ -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 Login(string username, SecureString password) { + var result = await client.MakeRequest("/auth/login", new(username, password)); + + if (!string.IsNullOrEmpty(result.Secret) && result.Profile != null && result.Account != null) + { + client.AuthSecret = result.Secret; + return result.Profile; + } + return null; } - public string Logout() + public async Task Logout() { - + await client.MakeRequest("/auth/logout", new()); + client.AuthSecret = null; + return true; } } diff --git a/OwnChar/Client/Data/Clients/UsersApiClient.cs b/OwnChar/Client/Data/Clients/UsersApiClient.cs index 0417f50..f1ddf28 100644 --- a/OwnChar/Client/Data/Clients/UsersApiClient.cs +++ b/OwnChar/Client/Data/Clients/UsersApiClient.cs @@ -1,5 +1,5 @@ namespace OwnChar.Client.Data.Clients; -public class UsersApiClient +public class UsersApiClient(OwnCharApiClient client) { } diff --git a/OwnChar/Client/Data/OwnCharApiClient.cs b/OwnChar/Client/Data/OwnCharApiClient.cs index eba1856..e1feeba 100644 --- a/OwnChar/Client/Data/OwnCharApiClient.cs +++ b/OwnChar/Client/Data/OwnCharApiClient.cs @@ -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 { private readonly HttpClient httpClient = new(); + private readonly Dictionary 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(); + Users = GetClient(); + Characters = GetClient(); + Groups = GetClient(); + } + + public T GetClient() 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 MakeRequest(string requestUrl, TRequest request) where TRequest : OwnCharRequest where TResponse : OwnCharResponse + { + if (await TryMakeRequest(requestUrl, request) is TResponse response) + return response; + throw new ApiException(string.Format("The api request to \"{0}\" failed!", requestUrl)); + } + + public async Task TryMakeRequest(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(); + + return null; + } } diff --git a/OwnChar/Client/IOwnCharManager.cs b/OwnChar/Client/IOwnCharManager.cs index 3fa9c17..733bdce 100644 --- a/OwnChar/Client/IOwnCharManager.cs +++ b/OwnChar/Client/IOwnCharManager.cs @@ -1,22 +1,21 @@ -using OwnChar.Client.Managers; -using OwnChar.Data; -using OwnChar.Model; +using OwnChar.Client.Data; +using OwnChar.Client.Data.Model; +using OwnChar.Client.Managers; using Pilz.Cryptography; namespace OwnChar.Client; public interface IOwnCharManager { - IDataManager? DataManager { get; set; } - UserAccountBase? CurrentUser { get; } bool IsLoggedIn { get; } + OwnCharApiClient? Api { get; } IUserManager Users { get; } IGroupsManager Groups { get; } ICharacterManager Characters { get; } - bool Login(IDataManager? proxy, string? username, SecureString? password); - bool Logout(); + Task Login(string? username, SecureString? password); + Task Logout(); public static IOwnCharManager CreateDefault() { diff --git a/OwnChar/Client/OwnCharManager.cs b/OwnChar/Client/OwnCharManager.cs index a84d08b..ca395bf 100644 --- a/OwnChar/Client/OwnCharManager.cs +++ b/OwnChar/Client/OwnCharManager.cs @@ -1,8 +1,7 @@ using OwnChar.Api.Exceptions; +using OwnChar.Client.Data; +using OwnChar.Client.Data.Model; using OwnChar.Client.Managers; -using OwnChar.Data; -using OwnChar.Manager.Modules; -using OwnChar.Model; using Pilz.Cryptography; using System.Diagnostics.CodeAnalysis; @@ -11,11 +10,10 @@ namespace OwnChar.Client; internal class OwnCharManager : IOwnCharManager { // User - public bool IsLoggedIn => CurrentUser != null; - public UserAccountBase? CurrentUser { get; private set; } + public bool IsLoggedIn => Api is not null && Api.IsLoggedIn; // Data Provider - public IDataManager? DataManager { get; set; } + public OwnCharApiClient? Api { get; private set; } // Manager public IUserManager Users { get; } @@ -29,10 +27,10 @@ internal class OwnCharManager : IOwnCharManager Characters = new CharacterManager(this); } - [MemberNotNull(nameof(CurrentUser), nameof(DataManager))] + [MemberNotNull(nameof(Api))] internal protected void CheckLogin() { - if (!IsLoggedIn || DataManager == null) + if (!IsLoggedIn) throw new LoginException("You are already logged in!"); } @@ -40,25 +38,25 @@ internal class OwnCharManager : IOwnCharManager /// Tries to login on the given data provider. /// /// Returns if the login was successfull and if not. - public bool Login(IDataManager? proxy, string? username, SecureString? password) + public Task Login(string? username, SecureString? password) { - ArgumentNullException.ThrowIfNull(proxy, nameof(proxy)); + ArgumentNullException.ThrowIfNull(Api, nameof(Api)); ArgumentException.ThrowIfNullOrWhiteSpace(username, nameof(username)); ArgumentException.ThrowIfNullOrWhiteSpace(password, nameof(password)); 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)); } /// /// Closes the session on the current data provider. /// /// - public bool Logout() + public async Task Logout() { - return DataManager?.Logout(CurrentUser) ?? true; + if (Api is null) + return true; + return await Api.Auth.Logout(); } }