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);
|
||||
var response = await _categoryApi.GetAllCategoriesAsync();
|
||||
var response = await _categoryApi.GetAllAsync();
|
||||
|
||||
response.Should().HaveCount(3);
|
||||
response.Should().SatisfyRespectively(c =>
|
||||
|
@ -1,10 +1,32 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Flurl.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Piwigo.Client.Contract;
|
||||
|
||||
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
|
||||
{
|
||||
private readonly IPiwigoContext _context;
|
||||
@ -16,11 +38,43 @@ public class CategoryApi : ICategoryApi
|
||||
_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");
|
||||
var response = await _context.ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.categories.getList").AddString("recursive", "true"));
|
||||
var typedResponse = await response.GetJsonAsync<PiwigoResponse<PiwigoCategoryList>>();
|
||||
return new ReadOnlyCollection<PiwigoCategory>(typedResponse.Result.Categories);
|
||||
var formParams = new Dictionary<string, string> { { "recursive", "true" } };
|
||||
var response = await _context.PostAsync<PiwigoResponse<PiwigoCategoryList>>(_logger, "pwg.categories.getList", formParams);
|
||||
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")]
|
||||
public string? Status { get; init; }
|
||||
|
||||
[JsonProperty("err")]
|
||||
public int? Error { get; init; }
|
||||
|
||||
[JsonProperty("message")]
|
||||
public string? Message { get; init; }
|
||||
|
||||
[JsonProperty("result")]
|
||||
public T Result { get; init; } = default!;
|
||||
}
|
@ -4,5 +4,8 @@ namespace Piwigo.Client;
|
||||
|
||||
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;
|
||||
|
||||
namespace Piwigo.Client;
|
||||
|
||||
public interface IPiwigoContext
|
||||
{
|
||||
IPiwigoConfiguration Config { get; }
|
||||
bool IsLoggedIn { get; }
|
||||
IFlurlRequest ConfigureRequest(ILogger logger, bool requireLogin = true);
|
||||
void LoggedOut();
|
||||
void LoggedIn();
|
||||
Task LoginAsync();
|
||||
Task LogoutAsync();
|
||||
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.Content;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Piwigo.Client;
|
||||
|
||||
public class PiwigoContext : IPiwigoContext
|
||||
{
|
||||
private readonly IPiwigoConfiguration _config;
|
||||
private readonly CookieJar _cookies = new();
|
||||
private readonly 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));
|
||||
}
|
||||
|
||||
public IPiwigoConfiguration Config { get; }
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
return _config.BaseUri.WithCookies(_cookies).ConfigureRequest(r => r.AfterCallAsync = call => LogResponse(call, 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 Piwigo.Client.Contract;
|
||||
|
||||
@ -16,52 +14,20 @@ internal class SessionApi : ISessionApi
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task LogoutAsync()
|
||||
public Task LogoutAsync()
|
||||
{
|
||||
_logger.LogInformation("Logging out from {Uri}", _context.Config.BaseUri);
|
||||
await _context.ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.session.logout"));
|
||||
_context.LoggedOut();
|
||||
return _context.LogoutAsync();
|
||||
}
|
||||
|
||||
public async Task LoginAsync()
|
||||
public 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();
|
||||
return _context.LoginAsync();
|
||||
}
|
||||
|
||||
public async Task<PiwigoStatus> GetStatusAsync()
|
||||
{
|
||||
_logger.LogInformation("Getting status");
|
||||
var response = await _context.ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.session.getStatus"));
|
||||
var typedResponse = await response.GetJsonAsync<PiwigoResponse<PiwigoStatus>>();
|
||||
var typedResponse = await _context.PostAsync<PiwigoResponse<PiwigoStatus>>(_logger, "pwg.session.getStatus");
|
||||
return typedResponse.Result;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user