moves login and logout to context and adds abstracted generic post method to hide flurl from api implementations
This commit is contained in:
parent
cae3f94e39
commit
62679b0c09
@ -33,7 +33,7 @@ public class CategoryApiTests : ApiTestsBase
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
SetJsonResult(serverResponse);
|
SetJsonResult(serverResponse);
|
||||||
var response = await _categoryApi.GetAllCategoriesAsync();
|
var response = await _categoryApi.GetAllAsync();
|
||||||
|
|
||||||
response.Should().HaveCount(3);
|
response.Should().HaveCount(3);
|
||||||
response.Should().SatisfyRespectively(c =>
|
response.Should().SatisfyRespectively(c =>
|
||||||
|
@ -1,10 +1,32 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using Flurl.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Piwigo.Client.Contract;
|
using Piwigo.Client.Contract;
|
||||||
|
|
||||||
namespace Piwigo.Client;
|
namespace Piwigo.Client;
|
||||||
|
|
||||||
|
internal static class DictionaryExtensions
|
||||||
|
{
|
||||||
|
public static IDictionary<string, string> AddIfValueNotNull(this IDictionary<string, string> dictionary, string key, string? value)
|
||||||
|
{
|
||||||
|
if (dictionary == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(dictionary));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Value cannot be null or whitespace.", nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is not null)
|
||||||
|
{
|
||||||
|
dictionary.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class CategoryApi : ICategoryApi
|
public class CategoryApi : ICategoryApi
|
||||||
{
|
{
|
||||||
private readonly IPiwigoContext _context;
|
private readonly IPiwigoContext _context;
|
||||||
@ -16,11 +38,43 @@ public class CategoryApi : ICategoryApi
|
|||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyCollection<PiwigoCategory>> GetAllCategoriesAsync()
|
public async Task<int> AddAsync(string name, int? parentId = null, string? comment = null, bool? visible = null, CategoryStatus? status = null, bool? commentable = null,
|
||||||
|
CategoryPosition? position = null)
|
||||||
|
{
|
||||||
|
var statusValue = status switch
|
||||||
|
{
|
||||||
|
CategoryStatus.Public => "public",
|
||||||
|
CategoryStatus.Private => "private",
|
||||||
|
null => null,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
|
||||||
|
};
|
||||||
|
|
||||||
|
var positionValue = position switch
|
||||||
|
{
|
||||||
|
CategoryPosition.First => "first",
|
||||||
|
CategoryPosition.Last => "last",
|
||||||
|
null => null,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(position), position, null)
|
||||||
|
};
|
||||||
|
|
||||||
|
var formParams = new Dictionary<string, string> { { "name", name } };
|
||||||
|
formParams.AddIfValueNotNull("parent", parentId?.ToString()).AddIfValueNotNull("comment", comment).AddIfValueNotNull("visible", visible?.ToString())
|
||||||
|
.AddIfValueNotNull("status", statusValue).AddIfValueNotNull("commentable", visible?.ToString()).AddIfValueNotNull("position", positionValue);
|
||||||
|
|
||||||
|
var response = await _context.PostAsync<PiwigoResponse<AlbumAdded>>(_logger, "pwg.categories.add", formParams);
|
||||||
|
if (!response.Result.Id.HasValue)
|
||||||
|
{
|
||||||
|
throw new PiwigoException($"Could not create album {name}: {response.Result.Info}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result.Id.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<PiwigoCategory>> GetAllAsync()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Getting all existing categories from server");
|
_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 formParams = new Dictionary<string, string> { { "recursive", "true" } };
|
||||||
var typedResponse = await response.GetJsonAsync<PiwigoResponse<PiwigoCategoryList>>();
|
var response = await _context.PostAsync<PiwigoResponse<PiwigoCategoryList>>(_logger, "pwg.categories.getList", formParams);
|
||||||
return new ReadOnlyCollection<PiwigoCategory>(typedResponse.Result.Categories);
|
return new ReadOnlyCollection<PiwigoCategory>(response.Result.Categories);
|
||||||
}
|
}
|
||||||
}
|
}
|
7
PiwigoDotnet/Piwigo.Client/CategoryPosition.cs
Normal file
7
PiwigoDotnet/Piwigo.Client/CategoryPosition.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Piwigo.Client;
|
||||||
|
|
||||||
|
public enum CategoryPosition
|
||||||
|
{
|
||||||
|
First = 0,
|
||||||
|
Last = 1
|
||||||
|
}
|
7
PiwigoDotnet/Piwigo.Client/CategoryStatus.cs
Normal file
7
PiwigoDotnet/Piwigo.Client/CategoryStatus.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Piwigo.Client;
|
||||||
|
|
||||||
|
public enum CategoryStatus
|
||||||
|
{
|
||||||
|
Public = 0,
|
||||||
|
Private = 1
|
||||||
|
}
|
12
PiwigoDotnet/Piwigo.Client/Contract/AlbumAdded.cs
Normal file
12
PiwigoDotnet/Piwigo.Client/Contract/AlbumAdded.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Piwigo.Client.Contract;
|
||||||
|
|
||||||
|
internal class AlbumAdded
|
||||||
|
{
|
||||||
|
[JsonProperty("info")]
|
||||||
|
public string? Info { get; init; }
|
||||||
|
|
||||||
|
[JsonProperty("id")]
|
||||||
|
public int? Id { get; init; }
|
||||||
|
}
|
@ -7,6 +7,12 @@ internal class PiwigoResponse<T>
|
|||||||
[JsonProperty("stat")]
|
[JsonProperty("stat")]
|
||||||
public string? Status { get; init; }
|
public string? Status { get; init; }
|
||||||
|
|
||||||
|
[JsonProperty("err")]
|
||||||
|
public int? Error { get; init; }
|
||||||
|
|
||||||
|
[JsonProperty("message")]
|
||||||
|
public string? Message { get; init; }
|
||||||
|
|
||||||
[JsonProperty("result")]
|
[JsonProperty("result")]
|
||||||
public T Result { get; init; } = default!;
|
public T Result { get; init; } = default!;
|
||||||
}
|
}
|
@ -4,5 +4,8 @@ namespace Piwigo.Client;
|
|||||||
|
|
||||||
public interface ICategoryApi
|
public interface ICategoryApi
|
||||||
{
|
{
|
||||||
Task<IReadOnlyCollection<PiwigoCategory>> GetAllCategoriesAsync();
|
Task<int> AddAsync(string name, int? parentId = null, string? comment = null, bool? visible = null, CategoryStatus? status = null, bool? commentable = null,
|
||||||
|
CategoryPosition? position = null);
|
||||||
|
|
||||||
|
Task<IReadOnlyCollection<PiwigoCategory>> GetAllAsync();
|
||||||
}
|
}
|
@ -1,13 +1,12 @@
|
|||||||
using Flurl.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Piwigo.Client;
|
namespace Piwigo.Client;
|
||||||
|
|
||||||
public interface IPiwigoContext
|
public interface IPiwigoContext
|
||||||
{
|
{
|
||||||
IPiwigoConfiguration Config { get; }
|
|
||||||
bool IsLoggedIn { get; }
|
bool IsLoggedIn { get; }
|
||||||
IFlurlRequest ConfigureRequest(ILogger logger, bool requireLogin = true);
|
Task LoginAsync();
|
||||||
void LoggedOut();
|
Task LogoutAsync();
|
||||||
void LoggedIn();
|
Task<T> PostAsync<T>(ILogger logger, string method, IDictionary<string, string> formParams);
|
||||||
|
Task<T> PostAsync<T>(ILogger logger, string method);
|
||||||
}
|
}
|
@ -1,49 +1,126 @@
|
|||||||
|
using System.Net;
|
||||||
using Flurl.Http;
|
using Flurl.Http;
|
||||||
|
using Flurl.Http.Content;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Piwigo.Client;
|
namespace Piwigo.Client;
|
||||||
|
|
||||||
public class PiwigoContext : IPiwigoContext
|
public class PiwigoContext : IPiwigoContext
|
||||||
{
|
{
|
||||||
|
private readonly IPiwigoConfiguration _config;
|
||||||
private readonly CookieJar _cookies = new();
|
private readonly CookieJar _cookies = new();
|
||||||
private readonly ILogger<PiwigoContext> _logger;
|
private readonly ILogger<PiwigoContext> _logger;
|
||||||
|
|
||||||
public PiwigoContext(IPiwigoConfiguration configuration, ILogger<PiwigoContext> logger)
|
public PiwigoContext(IPiwigoConfiguration configuration, ILogger<PiwigoContext> logger)
|
||||||
{
|
{
|
||||||
Config = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
_config = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPiwigoConfiguration Config { get; }
|
|
||||||
|
|
||||||
public bool IsLoggedIn { get; private set; }
|
public bool IsLoggedIn { get; private set; }
|
||||||
|
|
||||||
public IFlurlRequest ConfigureRequest(ILogger logger, bool requireLogin = true)
|
public async Task LoginAsync()
|
||||||
|
{
|
||||||
|
var userName = _config.UserName;
|
||||||
|
if (string.IsNullOrWhiteSpace(userName))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Value cannot be null or whitespace.", nameof(userName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var password = _config.Password;
|
||||||
|
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!");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Logging into {PiwigoBaseUri} using username {Username}", _config.BaseUri, userName);
|
||||||
|
|
||||||
|
var response = await ConfigureRequest(_logger).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 {_config.BaseUri} using username {userName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Logging in succeeded");
|
||||||
|
_logger.LogInformation("logged in");
|
||||||
|
IsLoggedIn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LogoutAsync()
|
||||||
|
{
|
||||||
|
if (!IsLoggedIn)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Tried to log out from {Uri} but was not logged in!", _config.BaseUri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Logging out from {Uri}", _config.BaseUri);
|
||||||
|
await ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.session.logout"));
|
||||||
|
_logger.LogInformation("logged out, clearing cookies");
|
||||||
|
IsLoggedIn = false;
|
||||||
|
_cookies.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> PostAsync<T>(ILogger logger, string method)
|
||||||
|
{
|
||||||
|
return PostInternalAsync<T>(logger, method, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> PostAsync<T>(ILogger logger, string method, IDictionary<string, string> formParams)
|
||||||
|
{
|
||||||
|
return PostInternalAsync<T>(logger, method, formParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddFormParams(CapturedMultipartContent c, IDictionary<string, string> formParams)
|
||||||
|
{
|
||||||
|
foreach (var formParam in formParams)
|
||||||
|
{
|
||||||
|
c.AddString(formParam.Key, formParam.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<T> PostInternalAsync<T>(ILogger logger, string method, IDictionary<string, string>? formParams)
|
||||||
|
{
|
||||||
|
await EnsureLoggedInAsync();
|
||||||
|
|
||||||
|
var response = await ConfigureRequest(logger).PostMultipartAsync(c =>
|
||||||
|
{
|
||||||
|
c.AddMethod(method);
|
||||||
|
if (formParams != null)
|
||||||
|
{
|
||||||
|
AddFormParams(c, formParams);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var typedResponse = await response.GetJsonAsync<T>();
|
||||||
|
return typedResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ValueTask EnsureLoggedInAsync()
|
||||||
|
{
|
||||||
|
if (IsLoggedIn)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await LoginAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IFlurlRequest ConfigureRequest(ILogger logger)
|
||||||
{
|
{
|
||||||
if (logger == null)
|
if (logger == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(logger));
|
throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requireLogin && !IsLoggedIn)
|
return _config.BaseUri.WithCookies(_cookies).ConfigureRequest(r => r.AfterCallAsync = call => LogResponse(call, logger));
|
||||||
{
|
|
||||||
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)
|
private static async Task LogResponse(FlurlCall call, ILogger logger)
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
using System.Net;
|
|
||||||
using Flurl.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Piwigo.Client.Contract;
|
using Piwigo.Client.Contract;
|
||||||
|
|
||||||
@ -16,52 +14,20 @@ internal class SessionApi : ISessionApi
|
|||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LogoutAsync()
|
public Task LogoutAsync()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Logging out from {Uri}", _context.Config.BaseUri);
|
return _context.LogoutAsync();
|
||||||
await _context.ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.session.logout"));
|
|
||||||
_context.LoggedOut();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoginAsync()
|
public Task LoginAsync()
|
||||||
{
|
{
|
||||||
var userName = _context.Config.UserName;
|
return _context.LoginAsync();
|
||||||
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<PiwigoStatus> GetStatusAsync()
|
public async Task<PiwigoStatus> GetStatusAsync()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Getting status");
|
_logger.LogInformation("Getting status");
|
||||||
var response = await _context.ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.session.getStatus"));
|
var typedResponse = await _context.PostAsync<PiwigoResponse<PiwigoStatus>>(_logger, "pwg.session.getStatus");
|
||||||
var typedResponse = await response.GetJsonAsync<PiwigoResponse<PiwigoStatus>>();
|
|
||||||
return typedResponse.Result;
|
return typedResponse.Result;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user