diff --git a/PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs b/PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs new file mode 100644 index 0000000..8a85bb4 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs @@ -0,0 +1,59 @@ +using Flurl.Http.Configuration; +using Flurl.Http.Testing; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; +using Piwigo.Client.Contract; + +namespace Piwigo.Client.Tests; + +public class ApiTestsBase +{ + private const string TestUri = "http://localhost:8080/ws.php?format=json"; + private const string Username = "admin"; + private const string Password = "admin"; + private const bool UseHttpTest = true; + protected HttpTest? HttpTest { get; private set; } + protected IPiwigoContext Context { get; private set; } = null!; + + [SetUp] + public void SetUp() + { + if (UseHttpTest) + { + HttpTest = new HttpTest(); + } + + Context = new PiwigoContext(new PiwigoConfiguration(TestUri, Username, Password), new NullLogger()); + OnSetUp(); + } + + protected virtual void OnSetUp() + { + } + + [TearDown] + public void TearDown() + { + HttpTest?.Dispose(); + } + + protected async Task LoginAsync() + { + HttpTest?.RespondWith("{}", 200, cookies: new { pwg_id = "pwg_id" }); + var sessionApi = new SessionApi(Context, new NullLogger()); + await sessionApi.LoginAsync(); + Context.IsLoggedIn.Should().BeTrue(); + } + + internal void SetJsonResult(PiwigoResponse serverResponse) + { + var settings = new JsonSerializerSettings + { + Formatting = Formatting.Indented, + NullValueHandling = NullValueHandling.Ignore + }; + var serializer = new NewtonsoftJsonSerializer(settings); + var jsonResponse = serializer.Serialize(serverResponse); + HttpTest?.RespondWith(jsonResponse); + } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client.Tests/CategoryApiTests.cs b/PiwigoDotnet/Piwigo.Client.Tests/CategoryApiTests.cs new file mode 100644 index 0000000..f30aad7 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client.Tests/CategoryApiTests.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.Logging.Abstractions; +using Piwigo.Client.Contract; + +namespace Piwigo.Client.Tests; + +[TestFixture] +public class CategoryApiTests : ApiTestsBase +{ + private CategoryApi _categoryApi = null!; + + protected override void OnSetUp() + { + base.OnSetUp(); + _categoryApi = new CategoryApi(Context, new NullLogger()); + } + + [Test] + public async Task GetAllCategories_should_return_all_existing_categories() + { + await LoginAsync(); + + + var serverResponse = new PiwigoResponse + { + Result = new PiwigoCategoryList + { + Categories = new List + { + new() { Id = 1, Name = "UnitTestMain" }, + new() { Id = 3, Name = "UnitTestSub2", IdUpperCat = 1 }, + new() { Id = 2, Name = "UnitTestSub1", IdUpperCat = 1 } + } + } + }; + SetJsonResult(serverResponse); + var response = await _categoryApi.GetAllCategoriesAsync(); + + response.Should().HaveCount(3); + response.Should().SatisfyRespectively(c => + { + c.Id.Should().Be(1); + c.Name.Should().Be("UnitTestMain"); + }, c => + { + c.Id.Should().Be(3); + c.Name.Should().Be("UnitTestSub2"); + c.IdUpperCat.Should().Be(1); + }, c => + { + c.Id.Should().Be(2); + c.Name.Should().Be("UnitTestSub1"); + c.IdUpperCat.Should().Be(1); + }); + } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client.Tests/PiwigoClientTests.cs b/PiwigoDotnet/Piwigo.Client.Tests/PiwigoClientTests.cs deleted file mode 100644 index e4f33d0..0000000 --- a/PiwigoDotnet/Piwigo.Client.Tests/PiwigoClientTests.cs +++ /dev/null @@ -1,128 +0,0 @@ -using Flurl.Http.Configuration; -using Flurl.Http.Testing; -using Microsoft.Extensions.Logging.Abstractions; -using Newtonsoft.Json; - -namespace Piwigo.Client.Tests; - -[TestFixture] -public class PiwigoClientTests -{ - private const string TestUri = "http://localhost:8080/ws.php?format=json"; - private const string Username = "admin"; - private const string Password = "admin"; - - private PiwigoClient _piwigoClient = null!; - private HttpTest? _httpTest; - - [SetUp] - public void SetUp() - { - _piwigoClient = new PiwigoClient(new NullLogger()); - _httpTest = new HttpTest(); - } - - [TearDown] - public void TearDown() - { - _httpTest?.Dispose(); - } - - [Test] - public async Task Login_should_set_cookies_and_session() - { - await LoginAsync(); - } - - [Test] - public async Task GetStatus_should_return_config() - { - await LoginAsync(); - - var serverResponse = new PiwigoResponse - { - Status = "OK", - Result = new PiwigoStatus - { - Username = "admin", - Version = "12.0.0" - } - }; - SetJsonResult(serverResponse); - - var status = await _piwigoClient.GetStatusAsync(); - - status.Should().NotBeNull(); - status.Username.Should().Be("admin"); - status.Version.Should().NotBeEmpty(); - } - - [Test] - public async Task Logout_should_set_IsLoggedIn_to_false() - { - await LoginAsync(); - - _httpTest?.RespondWith("OK"); - - await _piwigoClient.LogoutAsync(); - _piwigoClient.IsLoggedIn.Should().BeFalse(); - } - - [Test] - public async Task GetAllCategories_should_return_all_existing_categories() - { - await LoginAsync(); - - - var serverResponse = new PiwigoResponse - { - Result = new PiwigoCategoryList - { - Categories = new List - { - new() { Id = 1, Name = "UnitTestMain" }, - new() { Id = 3, Name = "UnitTestSub2", IdUpperCat = 1 }, - new() { Id = 2, Name = "UnitTestSub1", IdUpperCat = 1 } - } - } - }; - SetJsonResult(serverResponse); - var response = await _piwigoClient.GetAllCategoriesAsync(); - - response.Should().HaveCount(3); - response.Should().SatisfyRespectively(c => - { - c.Id.Should().Be(1); - c.Name.Should().Be("UnitTestMain"); - }, c => - { - c.Id.Should().Be(3); - c.Name.Should().Be("UnitTestSub2"); - c.IdUpperCat.Should().Be(1); - }, c => - { - c.Id.Should().Be(2); - c.Name.Should().Be("UnitTestSub1"); - c.IdUpperCat.Should().Be(1); - }); - } - - private void SetJsonResult(PiwigoResponse serverResponse) - { - var settings = new JsonSerializerSettings - { - Formatting = Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore - }; - var serializer = new NewtonsoftJsonSerializer(settings); - var jsonResponse = serializer.Serialize(serverResponse); - _httpTest?.RespondWith(jsonResponse); - } - - private async Task LoginAsync() - { - _httpTest?.RespondWith("{}", 200, cookies: new { pwg_id = "pwg_id" }); - await _piwigoClient.LoginAsync(new Uri(TestUri), Username, Password); - _piwigoClient.IsLoggedIn.Should().BeTrue(); - } -} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client.Tests/SessionApiTests.cs b/PiwigoDotnet/Piwigo.Client.Tests/SessionApiTests.cs new file mode 100644 index 0000000..34f21ef --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client.Tests/SessionApiTests.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.Logging.Abstractions; +using Piwigo.Client.Contract; + +namespace Piwigo.Client.Tests; + +[TestFixture] +public class SessionApiTests : ApiTestsBase +{ + private SessionApi _sessionApi = null!; + + protected override void OnSetUp() + { + base.OnSetUp(); + _sessionApi = new SessionApi(Context, new NullLogger()); + } + + [Test] + public async Task Login_should_set_cookies_and_session() + { + await LoginAsync(); + } + + [Test] + public async Task GetStatus_should_return_config() + { + await LoginAsync(); + var serverResponse = new PiwigoResponse + { + Status = "OK", + Result = new PiwigoStatus + { + Username = "admin", + Version = "12.0.0" + } + }; + SetJsonResult(serverResponse); + + var status = await _sessionApi.GetStatusAsync(); + + status.Should().NotBeNull(); + status.Username.Should().Be("admin"); + status.Version.Should().NotBeEmpty(); + } + + [Test] + public async Task Logout_should_set_IsLoggedIn_to_false() + { + await LoginAsync(); + + HttpTest?.RespondWith("OK"); + + await _sessionApi.LogoutAsync(); + Context.IsLoggedIn.Should().BeFalse(); + } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/CategoryApi.cs b/PiwigoDotnet/Piwigo.Client/CategoryApi.cs new file mode 100644 index 0000000..3d835b2 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/CategoryApi.cs @@ -0,0 +1,26 @@ +using System.Collections.ObjectModel; +using Flurl.Http; +using Microsoft.Extensions.Logging; +using Piwigo.Client.Contract; + +namespace Piwigo.Client; + +public class CategoryApi : ICategoryApi +{ + private readonly IPiwigoContext _context; + private readonly ILogger _logger; + + public CategoryApi(IPiwigoContext context, ILogger logger) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public async Task> GetAllCategoriesAsync() + { + _logger.LogInformation("Getting all existing categories from server"); + var response = await _context.ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.categories.getList").AddString("recursive", "true")); + var typedResponse = await response.GetJsonAsync>(); + return new ReadOnlyCollection(typedResponse.Result.Categories); + } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/PiwigoCategory.cs b/PiwigoDotnet/Piwigo.Client/Contract/PiwigoCategory.cs similarity index 97% rename from PiwigoDotnet/Piwigo.Client/PiwigoCategory.cs rename to PiwigoDotnet/Piwigo.Client/Contract/PiwigoCategory.cs index 37c2633..842255a 100644 --- a/PiwigoDotnet/Piwigo.Client/PiwigoCategory.cs +++ b/PiwigoDotnet/Piwigo.Client/Contract/PiwigoCategory.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; -namespace Piwigo.Client; +namespace Piwigo.Client.Contract; [SuppressMessage("ReSharper", "StringLiteralTypo")] public class PiwigoCategory diff --git a/PiwigoDotnet/Piwigo.Client/PiwigoCategoryList.cs b/PiwigoDotnet/Piwigo.Client/Contract/PiwigoCategoryList.cs similarity index 82% rename from PiwigoDotnet/Piwigo.Client/PiwigoCategoryList.cs rename to PiwigoDotnet/Piwigo.Client/Contract/PiwigoCategoryList.cs index 435b0ce..86b8b16 100644 --- a/PiwigoDotnet/Piwigo.Client/PiwigoCategoryList.cs +++ b/PiwigoDotnet/Piwigo.Client/Contract/PiwigoCategoryList.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace Piwigo.Client; +namespace Piwigo.Client.Contract; public class PiwigoCategoryList { diff --git a/PiwigoDotnet/Piwigo.Client/PiwigoResponse.cs b/PiwigoDotnet/Piwigo.Client/Contract/PiwigoResponse.cs similarity index 85% rename from PiwigoDotnet/Piwigo.Client/PiwigoResponse.cs rename to PiwigoDotnet/Piwigo.Client/Contract/PiwigoResponse.cs index bdb01dc..a28ebce 100644 --- a/PiwigoDotnet/Piwigo.Client/PiwigoResponse.cs +++ b/PiwigoDotnet/Piwigo.Client/Contract/PiwigoResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace Piwigo.Client; +namespace Piwigo.Client.Contract; internal class PiwigoResponse { diff --git a/PiwigoDotnet/Piwigo.Client/PiwigoStatus.cs b/PiwigoDotnet/Piwigo.Client/Contract/PiwigoStatus.cs similarity index 96% rename from PiwigoDotnet/Piwigo.Client/PiwigoStatus.cs rename to PiwigoDotnet/Piwigo.Client/Contract/PiwigoStatus.cs index 9589fbe..081888b 100644 --- a/PiwigoDotnet/Piwigo.Client/PiwigoStatus.cs +++ b/PiwigoDotnet/Piwigo.Client/Contract/PiwigoStatus.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace Piwigo.Client; +namespace Piwigo.Client.Contract; public class PiwigoStatus { diff --git a/PiwigoDotnet/Piwigo.Client/FlurlExtensions.cs b/PiwigoDotnet/Piwigo.Client/FlurlExtensions.cs new file mode 100644 index 0000000..6be5a5a --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/FlurlExtensions.cs @@ -0,0 +1,16 @@ +using Flurl.Http.Content; + +namespace Piwigo.Client; + +internal static class FlurlExtensions +{ + internal static CapturedMultipartContent AddMethod(this CapturedMultipartContent part, string piwigoMethod) + { + if (string.IsNullOrWhiteSpace(piwigoMethod)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(piwigoMethod)); + } + + return part.AddString("method", piwigoMethod); + } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/ICategoryApi.cs b/PiwigoDotnet/Piwigo.Client/ICategoryApi.cs new file mode 100644 index 0000000..8f3ef97 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/ICategoryApi.cs @@ -0,0 +1,8 @@ +using Piwigo.Client.Contract; + +namespace Piwigo.Client; + +public interface ICategoryApi +{ + Task> GetAllCategoriesAsync(); +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/IPiwigoClient.cs b/PiwigoDotnet/Piwigo.Client/IPiwigoClient.cs index 4c5e8df..d6c47f3 100644 --- a/PiwigoDotnet/Piwigo.Client/IPiwigoClient.cs +++ b/PiwigoDotnet/Piwigo.Client/IPiwigoClient.cs @@ -2,10 +2,6 @@ public interface IPiwigoClient { - bool IsLoggedIn { get; } - int ChunkSize { get; set; } - Task LoginAsync(Uri uri, string username, string password); - Task LogoutAsync(); - Task GetStatusAsync(); - Task> GetAllCategoriesAsync(); + ISessionApi Session { get; } + ICategoryApi Category { get; } } \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/IPiwigoConfiguration.cs b/PiwigoDotnet/Piwigo.Client/IPiwigoConfiguration.cs new file mode 100644 index 0000000..bfce506 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/IPiwigoConfiguration.cs @@ -0,0 +1,9 @@ +namespace Piwigo.Client; + +public interface IPiwigoConfiguration +{ + public int ChunkSize { get; } + public string BaseUri { get; } + public string UserName { get; } + public string Password { get; } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/IPiwigoContext.cs b/PiwigoDotnet/Piwigo.Client/IPiwigoContext.cs new file mode 100644 index 0000000..8ecf978 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/IPiwigoContext.cs @@ -0,0 +1,13 @@ +using Flurl.Http; +using Microsoft.Extensions.Logging; + +namespace Piwigo.Client; + +public interface IPiwigoContext +{ + IPiwigoConfiguration Config { get; } + bool IsLoggedIn { get; } + IFlurlRequest ConfigureRequest(ILogger logger, bool requireLogin = true); + void LoggedOut(); + void LoggedIn(); +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/ISessionApi.cs b/PiwigoDotnet/Piwigo.Client/ISessionApi.cs new file mode 100644 index 0000000..1d5eec5 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/ISessionApi.cs @@ -0,0 +1,10 @@ +using Piwigo.Client.Contract; + +namespace Piwigo.Client; + +public interface ISessionApi +{ + Task LoginAsync(); + Task LogoutAsync(); + Task GetStatusAsync(); +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/PiwigoClient.cs b/PiwigoDotnet/Piwigo.Client/PiwigoClient.cs index 8b646cb..279abd4 100644 --- a/PiwigoDotnet/Piwigo.Client/PiwigoClient.cs +++ b/PiwigoDotnet/Piwigo.Client/PiwigoClient.cs @@ -1,110 +1,13 @@ -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Net; -using System.Runtime.CompilerServices; -using Flurl; -using Flurl.Http; -using Microsoft.Extensions.Logging; - namespace Piwigo.Client; public class PiwigoClient : IPiwigoClient { - private CookieJar _cookies = new(); - private string _piwigoBaseUri = null!; - public bool IsLoggedIn { get; private set; } - public int ChunkSize { get; set; } = 512; - private IFlurlRequest Request => _piwigoBaseUri.WithCookies(_cookies).ConfigureRequest(r => r.AfterCallAsync = LogResponse); - - private readonly ILogger _logger; - - public PiwigoClient(ILogger logger) + public PiwigoClient(ISessionApi session, ICategoryApi category) { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + Session = session; + Category = category; } - public async Task LoginAsync(Uri uri, string username, string password) - { - if (uri == null) - { - throw new ArgumentNullException(nameof(uri)); - } - - if (string.IsNullOrWhiteSpace(username)) - { - throw new ArgumentException("Value cannot be null or whitespace.", nameof(username)); - } - - if (string.IsNullOrWhiteSpace(password)) - { - throw new ArgumentException("Value cannot be null or whitespace.", nameof(password)); - } - - if (IsLoggedIn) - { - throw new PiwigoException("The client is already logged in. Create a new instance or log out first!"); - } - - _piwigoBaseUri = uri.AppendPathSegment("ws.php").SetQueryParam("format", "json"); - - _logger.LogInformation("Logging into {PiwigoBaseUri} using username {Username}", _piwigoBaseUri, username); - - var response = await _piwigoBaseUri.WithCookies(out var cookieJar).PostMultipartAsync(c => - c.PiwigoLogin(username, password)); - - if (response.StatusCode != (int)HttpStatusCode.OK) - { - _logger.LogError("Failed to log in {StatusCode}", response.StatusCode); - throw new PiwigoException($"Could not log in to {_piwigoBaseUri} using username {username}"); - } - - _logger.LogInformation("Logging in succeeded"); - _cookies = cookieJar; - IsLoggedIn = true; - } - - public async Task LogoutAsync() - { - EnsureLoggedIn(); - - _logger.LogInformation("Logging out from {Uri}", _piwigoBaseUri); - await Request.PostMultipartAsync(c => c.PiwigoLogout()); - - IsLoggedIn = false; - _cookies.Clear(); - } - - public async Task GetStatusAsync() - { - EnsureLoggedIn(); - - _logger.LogInformation("Getting status"); - var response = await Request.PostMultipartAsync(c => c.PiwigoGetStatus()); - var typedResponse = await response.GetJsonAsync>(); - return typedResponse.Result; - } - - public async Task> GetAllCategoriesAsync() - { - EnsureLoggedIn(); - - _logger.LogInformation("Getting all existing categories from server"); - var response = await Request.PostMultipartAsync(c => c.PiwigoGetAllCategories()); - var typedResponse = await response.GetJsonAsync>(); - return new ReadOnlyCollection(typedResponse.Result.Categories); - } - - private void EnsureLoggedIn([CallerMemberName] string? callerName = null) - { - if (!IsLoggedIn) - { - throw new InvalidOperationException($"Could not execute {callerName} as the client is not logged in."); - } - } - - private async Task LogResponse(FlurlCall call) - { - var responseString = await call.Response.GetStringAsync(); - _logger.LogDebug("PiwigoResponse: {Response}", responseString); - } + public ISessionApi Session { get; } + public ICategoryApi Category { get; } } \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/PiwigoConfiguration.cs b/PiwigoDotnet/Piwigo.Client/PiwigoConfiguration.cs new file mode 100644 index 0000000..2300027 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/PiwigoConfiguration.cs @@ -0,0 +1,31 @@ +namespace Piwigo.Client; + +public class PiwigoConfiguration : IPiwigoConfiguration +{ + public PiwigoConfiguration(string baseUri, string userName, string password) + { + if (string.IsNullOrWhiteSpace(baseUri)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(baseUri)); + } + + if (string.IsNullOrWhiteSpace(userName)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(userName)); + } + + if (string.IsNullOrWhiteSpace(password)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(password)); + } + + BaseUri = baseUri; + UserName = userName; + Password = password; + } + + public int ChunkSize { get; set; } = 512; + public string BaseUri { get; } + public string UserName { get; } + public string Password { get; } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/PiwigoContext.cs b/PiwigoDotnet/Piwigo.Client/PiwigoContext.cs new file mode 100644 index 0000000..b52ba9d --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/PiwigoContext.cs @@ -0,0 +1,54 @@ +using Flurl.Http; +using Microsoft.Extensions.Logging; + +namespace Piwigo.Client; + +public class PiwigoContext : IPiwigoContext +{ + private readonly CookieJar _cookies = new(); + private readonly ILogger _logger; + + public PiwigoContext(IPiwigoConfiguration configuration, ILogger logger) + { + Config = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public IPiwigoConfiguration Config { get; } + + public bool IsLoggedIn { get; private set; } + + public IFlurlRequest ConfigureRequest(ILogger logger, bool requireLogin = true) + { + if (logger == null) + { + throw new ArgumentNullException(nameof(logger)); + } + + if (requireLogin && !IsLoggedIn) + { + throw new InvalidOperationException("User is not logged in. Ensure login is called before accessing any piwigo methods"); + } + + return Config.BaseUri.WithCookies(_cookies).ConfigureRequest(r => r.AfterCallAsync = call => LogResponse(call, logger)); + } + + public void LoggedOut() + { + _logger.LogInformation("logged out, clearing cookies"); + IsLoggedIn = false; + _cookies.Clear(); + } + + public void LoggedIn() + { + _logger.LogInformation("logged in"); + IsLoggedIn = true; + } + + private static async Task LogResponse(FlurlCall call, ILogger logger) + { + var responseString = await call.Response.GetStringAsync(); + logger.LogDebug("PiwigoResponse: {Response}", responseString); + } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/PiwigoMethods.cs b/PiwigoDotnet/Piwigo.Client/PiwigoMethods.cs deleted file mode 100644 index 2aaed46..0000000 --- a/PiwigoDotnet/Piwigo.Client/PiwigoMethods.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Flurl.Http.Content; - -namespace Piwigo.Client; - -internal static class PiwigoMethods -{ - public static void PiwigoLogin(this CapturedMultipartContent part, string username, - string password) - { - part.AddMethod("pwg.session.login").AddString("username", username).AddString("password", password); - } - - public static void PiwigoLogout(this CapturedMultipartContent part) - { - part.AddMethod("pwg.session.logout"); - } - - public static void PiwigoGetStatus(this CapturedMultipartContent part) - { - part.AddMethod("pwg.session.getStatus"); - } - - public static void PiwigoGetAllCategories(this CapturedMultipartContent part, bool recursive = true) - { - part.AddMethod("pwg.categories.getList").AddString("recursive", recursive.ToString()); - } - - private static CapturedMultipartContent AddMethod(this CapturedMultipartContent part, string piwigoMethod) - { - if (string.IsNullOrWhiteSpace(piwigoMethod)) - { - throw new ArgumentException("Value cannot be null or whitespace.", nameof(piwigoMethod)); - } - - return part.AddString("method", piwigoMethod); - } -} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/SessionApi.cs b/PiwigoDotnet/Piwigo.Client/SessionApi.cs new file mode 100644 index 0000000..e98a03b --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/SessionApi.cs @@ -0,0 +1,67 @@ +using System.Net; +using Flurl.Http; +using Microsoft.Extensions.Logging; +using Piwigo.Client.Contract; + +namespace Piwigo.Client; + +internal class SessionApi : ISessionApi +{ + private readonly IPiwigoContext _context; + private readonly ILogger _logger; + + public SessionApi(IPiwigoContext context, ILogger logger) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public async Task LogoutAsync() + { + _logger.LogInformation("Logging out from {Uri}", _context.Config.BaseUri); + await _context.ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.session.logout")); + _context.LoggedOut(); + } + + public async Task LoginAsync() + { + var userName = _context.Config.UserName; + if (string.IsNullOrWhiteSpace(userName)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(userName)); + } + + var password = _context.Config.Password; + if (string.IsNullOrWhiteSpace(password)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(password)); + } + + if (_context.IsLoggedIn) + { + throw new PiwigoException("The client is already logged in. Create a new instance or log out first!"); + } + + _logger.LogInformation("Logging into {PiwigoBaseUri} using username {Username}", _context.Config.BaseUri, userName); + + var response = await _context.ConfigureRequest(_logger, false).PostMultipartAsync(c => + c.AddMethod("pwg.session.login").AddString("username", userName).AddString("password", password)); + + if (response.StatusCode != (int)HttpStatusCode.OK) + { + _logger.LogError("Failed to log in {StatusCode}", response.StatusCode); + throw new PiwigoException($"Could not log in to {_context.Config.BaseUri} using username {userName}"); + } + + _logger.LogInformation("Logging in succeeded"); + _context.LoggedIn(); + } + + public async Task GetStatusAsync() + { + _logger.LogInformation("Getting status"); + var response = await _context.ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.session.getStatus")); + var typedResponse = await response.GetJsonAsync>(); + return typedResponse.Result; + } +} \ No newline at end of file diff --git a/PiwigoDotnet/PiwigoDotnet.sln.DotSettings b/PiwigoDotnet/PiwigoDotnet.sln.DotSettings index 82ba786..b1e22f3 100644 --- a/PiwigoDotnet/PiwigoDotnet.sln.DotSettings +++ b/PiwigoDotnet/PiwigoDotnet.sln.DotSettings @@ -1,4 +1,61 @@  + <?xml version="1.0" encoding="utf-16"?><Profile name="PiwigoCleanup"><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><CppAddTypenameTemplateKeywords>True</CppAddTypenameTemplateKeywords><CppRemoveCastDescriptor>True</CppRemoveCastDescriptor><CppRemoveElseKeyword>True</CppRemoveElseKeyword><CppShortenQualifiedName>True</CppShortenQualifiedName><CppDeleteRedundantSpecifier>True</CppDeleteRedundantSpecifier><CppRemoveStatement>True</CppRemoveStatement><CppDeleteRedundantTypenameTemplateKeywords>True</CppDeleteRedundantTypenameTemplateKeywords><CppCStyleToStaticCastDescriptor>True</CppCStyleToStaticCastDescriptor><CppReplaceExpressionWithBooleanConst>True</CppReplaceExpressionWithBooleanConst><CppMakeIfConstexpr>True</CppMakeIfConstexpr><CppMakePostfixOperatorPrefix>True</CppMakePostfixOperatorPrefix><CppChangeSmartPointerToMakeFunction>True</CppChangeSmartPointerToMakeFunction><CppReplaceThrowWithRethrowFix>True</CppReplaceThrowWithRethrowFix><CppTypeTraitAliasDescriptor>True</CppTypeTraitAliasDescriptor><CppReplaceExpressionWithNullptr>True</CppReplaceExpressionWithNullptr><CppReplaceTieWithStructuredBindingDescriptor>True</CppReplaceTieWithStructuredBindingDescriptor><CppUseAssociativeContainsDescriptor>True</CppUseAssociativeContainsDescriptor><CppUseEraseAlgorithmDescriptor>True</CppUseEraseAlgorithmDescriptor><CppCodeStyleCleanupDescriptor ArrangeBraces="True" ArrangeAuto="True" ArrangeFunctionDeclarations="True" ArrangeNestedNamespaces="True" ArrangeTypeAliases="True" ArrangeCVQualifiers="True" ArrangeSlashesInIncludeDirectives="True" ArrangeOverridingFunctions="True" SortIncludeDirectives="True" SortMemberInitializers="True" /><CppReformatCode>True</CppReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><CSCodeStyleAttributes ArrangeVarStyle="True" ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" ArrangeArgumentsStyle="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeCodeBodyStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" /><RemoveCodeRedundanciesVB>True</RemoveCodeRedundanciesVB><Xaml.RedundantFreezeAttribute>True</Xaml.RedundantFreezeAttribute><Xaml.RemoveRedundantModifiersAttribute>True</Xaml.RemoveRedundantModifiersAttribute><Xaml.RemoveRedundantNameAttribute>True</Xaml.RemoveRedundantNameAttribute><Xaml.RemoveRedundantResource>True</Xaml.RemoveRedundantResource><Xaml.RemoveRedundantCollectionProperty>True</Xaml.RemoveRedundantCollectionProperty><Xaml.RemoveRedundantAttachedPropertySetter>True</Xaml.RemoveRedundantAttachedPropertySetter><Xaml.RemoveRedundantStyledValue>True</Xaml.RemoveRedundantStyledValue><Xaml.RemoveRedundantNamespaceAlias>True</Xaml.RemoveRedundantNamespaceAlias><Xaml.RemoveForbiddenResourceName>True</Xaml.RemoveForbiddenResourceName><Xaml.RemoveRedundantGridDefinitionsAttribute>True</Xaml.RemoveRedundantGridDefinitionsAttribute><Xaml.RemoveRedundantUpdateSourceTriggerAttribute>True</Xaml.RemoveRedundantUpdateSourceTriggerAttribute><Xaml.RemoveRedundantBindingModeAttribute>True</Xaml.RemoveRedundantBindingModeAttribute><Xaml.RemoveRedundantGridSpanAttribut>True</Xaml.RemoveRedundantGridSpanAttribut><XMLReformatCode>True</XMLReformatCode><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><HtmlReformatCode>True</HtmlReformatCode><VBOptimizeImports>True</VBOptimizeImports><VBShortenReferences>True</VBShortenReferences><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><VBReformatCode>True</VBReformatCode><VBFormatDocComments>True</VBFormatDocComments><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><IDEA_SETTINGS>&lt;profile version="1.0"&gt; + &lt;option name="myName" value="PiwigoCleanup" /&gt; +&lt;/profile&gt;</IDEA_SETTINGS><RIDER_SETTINGS>&lt;profile&gt; + &lt;Language id="CSS"&gt; + &lt;Rearrange&gt;true&lt;/Rearrange&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="EditorConfig"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="HTML"&gt; + &lt;OptimizeImports&gt;true&lt;/OptimizeImports&gt; + &lt;Rearrange&gt;true&lt;/Rearrange&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="HTTP Request"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="Handlebars"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="Ini"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="JSON"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="Jade"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="JavaScript"&gt; + &lt;OptimizeImports&gt;true&lt;/OptimizeImports&gt; + &lt;Rearrange&gt;true&lt;/Rearrange&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="Markdown"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="Properties"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="RELAX-NG"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="SQL"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="XML"&gt; + &lt;OptimizeImports&gt;true&lt;/OptimizeImports&gt; + &lt;Rearrange&gt;true&lt;/Rearrange&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="yaml"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; +&lt;/profile&gt;</RIDER_SETTINGS></Profile> + PiwigoCleanup Required Required Required @@ -6,4 +63,8 @@ NEVER NEVER 180 + True + True + True + True True \ No newline at end of file