diff --git a/PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs b/PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs index 32594a5..8301d42 100644 --- a/PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs +++ b/PiwigoDotnet/Piwigo.Client.Tests/ApiTestsBase.cs @@ -39,12 +39,12 @@ public class ApiTestsBase HttpTest?.Dispose(); } - internal void SetOkResult() + protected void SetOkResult() { SetJsonResult(@"{stat: ""ok"", result: null}"); } - internal async Task SetJsonResultFromFileAsync(string fileName) + protected async Task SetJsonResultFromFileAsync(string fileName) { var assemblyLocation = Assembly.GetExecutingAssembly().Location; var directory = Path.GetDirectoryName(assemblyLocation) ?? Environment.CurrentDirectory; @@ -57,7 +57,7 @@ public class ApiTestsBase SetJsonResult(fileContent); } - internal void SetJsonResult(string json) + protected void SetJsonResult(string json) { HttpTest?.RespondWith(json); } @@ -67,11 +67,21 @@ public class ApiTestsBase CorrectParamShouldGetSent("method", methodName); } - protected void CorrectParamShouldGetSent(string paramName, string methodName) + protected void ParamShouldNotGetSent(string paramName) + { + HttpTest?.ShouldHaveMadeACall().With(c => + { + return !c.HttpRequestMessage.Content.As().Parts.OfType() + .Select(p => new { p.Headers.ContentDisposition?.Name, p.Content }) + .Any(s => s.Name?.Equals(paramName) ?? false); + }); + } + + protected void CorrectParamShouldGetSent(string paramName, string expectedValue) { HttpTest?.ShouldHaveMadeACall().With(c => c.HttpRequestMessage.Content.As().Parts.OfType().Select(p => new { p.Headers.ContentDisposition?.Name, p.Content }) - .Where(s => s.Name?.Equals(paramName) ?? false).Any(s => s.Content.Equals(methodName, StringComparison.OrdinalIgnoreCase))); + .Where(s => s.Name?.Equals(paramName) ?? false).Any(s => s.Content.Equals(expectedValue, StringComparison.OrdinalIgnoreCase))); } internal void SetJsonResult(PiwigoResponse serverResponse) diff --git a/PiwigoDotnet/Piwigo.Client.Tests/ImageApiTests.cs b/PiwigoDotnet/Piwigo.Client.Tests/ImageApiTests.cs index 63e7ddb..09f02dc 100644 --- a/PiwigoDotnet/Piwigo.Client.Tests/ImageApiTests.cs +++ b/PiwigoDotnet/Piwigo.Client.Tests/ImageApiTests.cs @@ -15,6 +15,55 @@ public class ImageApiTests : ApiTestsBase _imageApi = new ImageApi(Context, new NullLogger()); } + [Test] + public async Task Add() + { + SetJsonResult(@"{stat: ""ok"", + result: { + image_id: 1042, + url: ""https://localhost/image.jpg"" + }}"); + + var albums = new List<(int AlbumId, int? Rank)> + { + new ValueTuple(3, 10), + new ValueTuple(5, null), + new ValueTuple(7, 11) + }; + + var imageUpload = new ImageUpload("md5Sum") + { + Author = "unit test", + Comment = "perfect image", + Level = 42, + Name = "Image001.jpg", + CreatedAt = new DateTime(2022, 10, 22, 21, 50, 42), + OriginalFileName = "RAW-Image001.jpg", + Albums = albums, + TagIds = new List { 2, 4, 8 } + }; + + var uploaded = await _imageApi.Add(imageUpload); + + uploaded.ImageId.Should().Be(1042); + uploaded.Url.Should().Be("https://localhost/image.jpg"); + + CorrectMethodShouldGetCalled("pwg.images.add"); + + // must not be sent or it will be used as an update. + // Piwigo uses the same request for add or update depending on this parameter + ParamShouldNotGetSent("image_id"); + + CorrectParamShouldGetSent("original_filename", imageUpload.OriginalFileName); + CorrectParamShouldGetSent("name", imageUpload.Name); + CorrectParamShouldGetSent("author", imageUpload.Author); + CorrectParamShouldGetSent("date_creation", "2022-10-22 21:50:42"); + CorrectParamShouldGetSent("comment", imageUpload.Comment); + CorrectParamShouldGetSent("level", imageUpload.Level.ToString()!); + CorrectParamShouldGetSent("categories", "3,10;5;7,11"); + CorrectParamShouldGetSent("tag_ids", "2,4,8"); + } + [Test] public async Task ReadyForUpload_should_return_correct_value() { diff --git a/PiwigoDotnet/Piwigo.Client/Contract/ImageUpload.cs b/PiwigoDotnet/Piwigo.Client/Contract/ImageUpload.cs new file mode 100644 index 0000000..7e50a95 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/Contract/ImageUpload.cs @@ -0,0 +1,13 @@ +namespace Piwigo.Client.Contract; + +public record ImageUpload(string OriginalSum) +{ + public string? OriginalFileName { get; init; } + public string? Name { get; init; } + public string? Author { get; init; } + public DateTime? CreatedAt { get; init; } + public string? Comment { get; init; } + public int? Level { get; init; } + public IReadOnlyCollection<(int AlbumId, int? Rank)>? Albums { get; init; } + public IReadOnlyCollection? TagIds { get; init; } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/Contract/ImageUploaded.cs b/PiwigoDotnet/Piwigo.Client/Contract/ImageUploaded.cs new file mode 100644 index 0000000..a948ef9 --- /dev/null +++ b/PiwigoDotnet/Piwigo.Client/Contract/ImageUploaded.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Piwigo.Client.Contract; + +public class ImageUploaded +{ + [JsonProperty("image_id")] + public int? ImageId { get; init; } + + [JsonProperty("url")] + public string? Url { get; init; } +} \ No newline at end of file diff --git a/PiwigoDotnet/Piwigo.Client/IImageApi.cs b/PiwigoDotnet/Piwigo.Client/IImageApi.cs index 7393a52..c0dd145 100644 --- a/PiwigoDotnet/Piwigo.Client/IImageApi.cs +++ b/PiwigoDotnet/Piwigo.Client/IImageApi.cs @@ -4,18 +4,17 @@ namespace Piwigo.Client; public interface IImageApi { + Task Add(ImageUpload imageUpload, CancellationToken cancellationToken = default); Task AddChunkAsync(byte[] data, string originalSum, int position, CancellationToken cancellationToken = default); Task ReadyForUploadAsync(CancellationToken cancellationToken = default); - + Task GetImages(int albumId, bool recursive, PagingInfo page, ImageFilter filter, ImageOrder order = ImageOrder.Name, CancellationToken cancellationToken = default); - + /* -add addComment addFile -addSimple checkFiles delete deleteOrphans diff --git a/PiwigoDotnet/Piwigo.Client/ImageApi.cs b/PiwigoDotnet/Piwigo.Client/ImageApi.cs index fb19b8e..ebc0bf3 100644 --- a/PiwigoDotnet/Piwigo.Client/ImageApi.cs +++ b/PiwigoDotnet/Piwigo.Client/ImageApi.cs @@ -6,6 +6,9 @@ namespace Piwigo.Client; public class ImageApi : IImageApi { + private const string DateFormat = "yyyy-MM-dd"; + private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + private readonly IPiwigoContext _context; private readonly ILogger _logger; @@ -31,6 +34,32 @@ public class ImageApi : IImageApi await _context.PostAsync(_logger, "pwg.images.addChunk", formParams, cancellationToken); } + public async Task Add(ImageUpload imageUpload, CancellationToken cancellationToken = default) + { + var formParams = new Dictionary + { + { "original_sum", imageUpload.OriginalSum }, + { "check_uniqueness", "true" } + }; + + formParams.AddIfValueNotNull("original_filename", imageUpload.OriginalFileName); + formParams.AddIfValueNotNull("name", imageUpload.Name); + formParams.AddIfValueNotNull("author", imageUpload.Author); + formParams.AddIfValueNotNull("date_creation", imageUpload.CreatedAt?.ToString(DateTimeFormat)); + formParams.AddIfValueNotNull("comment", imageUpload.Comment); + formParams.AddIfValueNotNull("level", imageUpload.Level?.ToString()); + + var albums = imageUpload.Albums != null ? string.Join(";", imageUpload.Albums.Select(a => a.Rank.HasValue ? $"{a.AlbumId},{a.Rank}" : $"{a.AlbumId}")) : null; + formParams.AddIfValueNotNull("categories", albums); + + var tags = imageUpload.TagIds != null ? string.Join(",", imageUpload.TagIds.Select(t => t.ToString())) : null; + formParams.AddIfValueNotNull("tag_ids", tags); + + var response = await _context.PostAsync>(_logger, "pwg.images.add", formParams, cancellationToken); + + return response.Result; + } + public async Task ReadyForUploadAsync(CancellationToken cancellationToken = default) { var response = await _context.PostAsync>(_logger, "pwg.images.checkUpload", new Dictionary(), cancellationToken); @@ -99,22 +128,22 @@ public class ImageApi : IImageApi if (filter.MinDateAvailable.HasValue) { - formParams.Add("f_min_date_available", filter.MinDateAvailable.Value.ToString("yyyy-MM-dd")); + formParams.Add("f_min_date_available", filter.MinDateAvailable.Value.ToString(DateFormat)); } if (filter.MaxDataAvailable.HasValue) { - formParams.Add("f_max_date_available", filter.MaxDataAvailable.Value.ToString("yyyy-MM-dd")); + formParams.Add("f_max_date_available", filter.MaxDataAvailable.Value.ToString(DateFormat)); } if (filter.MinDateCreated.HasValue) { - formParams.Add("f_min_date_created", filter.MinDateCreated.Value.ToString("yyyy-MM-dd")); + formParams.Add("f_min_date_created", filter.MinDateCreated.Value.ToString(DateFormat)); } if (filter.MaxDateCreated.HasValue) { - formParams.Add("f_max_date_created", filter.MaxDateCreated.Value.ToString("yyyy-MM-dd")); + formParams.Add("f_max_date_created", filter.MaxDateCreated.Value.ToString(DateFormat)); } var response = await _context.PostAsync>(_logger, "pwg.categories.getImages", formParams, cancellationToken);