splits client into multiple interfaces and adds a context
adds code format for rider / resharper
This commit is contained in:
parent
b2940dc33b
commit
e5ba4c6e4a
59
PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs
Normal file
59
PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs
Normal file
@ -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<PiwigoContext>());
|
||||
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<SessionApi>());
|
||||
await sessionApi.LoginAsync();
|
||||
Context.IsLoggedIn.Should().BeTrue();
|
||||
}
|
||||
|
||||
internal void SetJsonResult<T>(PiwigoResponse<T> serverResponse)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
};
|
||||
var serializer = new NewtonsoftJsonSerializer(settings);
|
||||
var jsonResponse = serializer.Serialize(serverResponse);
|
||||
HttpTest?.RespondWith(jsonResponse);
|
||||
}
|
||||
}
|
55
PiwigoDotnet/Piwigo.Client.Tests/CategoryApiTests.cs
Normal file
55
PiwigoDotnet/Piwigo.Client.Tests/CategoryApiTests.cs
Normal file
@ -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<CategoryApi>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetAllCategories_should_return_all_existing_categories()
|
||||
{
|
||||
await LoginAsync();
|
||||
|
||||
|
||||
var serverResponse = new PiwigoResponse<PiwigoCategoryList>
|
||||
{
|
||||
Result = new PiwigoCategoryList
|
||||
{
|
||||
Categories = new List<PiwigoCategory>
|
||||
{
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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<PiwigoClient>());
|
||||
_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<PiwigoStatus>
|
||||
{
|
||||
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<PiwigoCategoryList>
|
||||
{
|
||||
Result = new PiwigoCategoryList
|
||||
{
|
||||
Categories = new List<PiwigoCategory>
|
||||
{
|
||||
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<T>(PiwigoResponse<T> 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();
|
||||
}
|
||||
}
|
55
PiwigoDotnet/Piwigo.Client.Tests/SessionApiTests.cs
Normal file
55
PiwigoDotnet/Piwigo.Client.Tests/SessionApiTests.cs
Normal file
@ -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<SessionApi>());
|
||||
}
|
||||
|
||||
[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<PiwigoStatus>
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
26
PiwigoDotnet/Piwigo.Client/CategoryApi.cs
Normal file
26
PiwigoDotnet/Piwigo.Client/CategoryApi.cs
Normal file
@ -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<CategoryApi> _logger;
|
||||
|
||||
public CategoryApi(IPiwigoContext context, ILogger<CategoryApi> logger)
|
||||
{
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<PiwigoCategory>> 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<PiwigoResponse<PiwigoCategoryList>>();
|
||||
return new ReadOnlyCollection<PiwigoCategory>(typedResponse.Result.Categories);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Piwigo.Client;
|
||||
namespace Piwigo.Client.Contract;
|
||||
|
||||
[SuppressMessage("ReSharper", "StringLiteralTypo")]
|
||||
public class PiwigoCategory
|
@ -1,6 +1,6 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Piwigo.Client;
|
||||
namespace Piwigo.Client.Contract;
|
||||
|
||||
public class PiwigoCategoryList
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Piwigo.Client;
|
||||
namespace Piwigo.Client.Contract;
|
||||
|
||||
internal class PiwigoResponse<T>
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Piwigo.Client;
|
||||
namespace Piwigo.Client.Contract;
|
||||
|
||||
public class PiwigoStatus
|
||||
{
|
16
PiwigoDotnet/Piwigo.Client/FlurlExtensions.cs
Normal file
16
PiwigoDotnet/Piwigo.Client/FlurlExtensions.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
8
PiwigoDotnet/Piwigo.Client/ICategoryApi.cs
Normal file
8
PiwigoDotnet/Piwigo.Client/ICategoryApi.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using Piwigo.Client.Contract;
|
||||
|
||||
namespace Piwigo.Client;
|
||||
|
||||
public interface ICategoryApi
|
||||
{
|
||||
Task<IReadOnlyCollection<PiwigoCategory>> GetAllCategoriesAsync();
|
||||
}
|
@ -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<PiwigoStatus> GetStatusAsync();
|
||||
Task<IReadOnlyCollection<PiwigoCategory>> GetAllCategoriesAsync();
|
||||
ISessionApi Session { get; }
|
||||
ICategoryApi Category { get; }
|
||||
}
|
9
PiwigoDotnet/Piwigo.Client/IPiwigoConfiguration.cs
Normal file
9
PiwigoDotnet/Piwigo.Client/IPiwigoConfiguration.cs
Normal file
@ -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; }
|
||||
}
|
13
PiwigoDotnet/Piwigo.Client/IPiwigoContext.cs
Normal file
13
PiwigoDotnet/Piwigo.Client/IPiwigoContext.cs
Normal file
@ -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();
|
||||
}
|
10
PiwigoDotnet/Piwigo.Client/ISessionApi.cs
Normal file
10
PiwigoDotnet/Piwigo.Client/ISessionApi.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using Piwigo.Client.Contract;
|
||||
|
||||
namespace Piwigo.Client;
|
||||
|
||||
public interface ISessionApi
|
||||
{
|
||||
Task LoginAsync();
|
||||
Task LogoutAsync();
|
||||
Task<PiwigoStatus> GetStatusAsync();
|
||||
}
|
@ -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<PiwigoClient> _logger;
|
||||
|
||||
public PiwigoClient(ILogger<PiwigoClient> 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<PiwigoStatus> GetStatusAsync()
|
||||
{
|
||||
EnsureLoggedIn();
|
||||
|
||||
_logger.LogInformation("Getting status");
|
||||
var response = await Request.PostMultipartAsync(c => c.PiwigoGetStatus());
|
||||
var typedResponse = await response.GetJsonAsync<PiwigoResponse<PiwigoStatus>>();
|
||||
return typedResponse.Result;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<PiwigoCategory>> GetAllCategoriesAsync()
|
||||
{
|
||||
EnsureLoggedIn();
|
||||
|
||||
_logger.LogInformation("Getting all existing categories from server");
|
||||
var response = await Request.PostMultipartAsync(c => c.PiwigoGetAllCategories());
|
||||
var typedResponse = await response.GetJsonAsync<PiwigoResponse<PiwigoCategoryList>>();
|
||||
return new ReadOnlyCollection<PiwigoCategory>(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; }
|
||||
}
|
31
PiwigoDotnet/Piwigo.Client/PiwigoConfiguration.cs
Normal file
31
PiwigoDotnet/Piwigo.Client/PiwigoConfiguration.cs
Normal file
@ -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; }
|
||||
}
|
54
PiwigoDotnet/Piwigo.Client/PiwigoContext.cs
Normal file
54
PiwigoDotnet/Piwigo.Client/PiwigoContext.cs
Normal file
@ -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<PiwigoContext> _logger;
|
||||
|
||||
public PiwigoContext(IPiwigoConfiguration configuration, ILogger<PiwigoContext> 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
67
PiwigoDotnet/Piwigo.Client/SessionApi.cs
Normal file
67
PiwigoDotnet/Piwigo.Client/SessionApi.cs
Normal file
@ -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<SessionApi> _logger;
|
||||
|
||||
public SessionApi(IPiwigoContext context, ILogger<SessionApi> 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<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>>();
|
||||
return typedResponse.Result;
|
||||
}
|
||||
}
|
@ -1,4 +1,61 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=PiwigoCleanup/@EntryIndexedValue"><?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></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">PiwigoCleanup</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
|
||||
@ -6,4 +63,8 @@
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">180</s:Int64>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Piwigo/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
Loading…
Reference in New Issue
Block a user