diff --git a/src/protagonist/Orchestrator.Tests/Integration/ManifestHandlingTests.cs b/src/protagonist/Orchestrator.Tests/Integration/ManifestHandlingTests.cs index 719ba18af..1d57861e5 100644 --- a/src/protagonist/Orchestrator.Tests/Integration/ManifestHandlingTests.cs +++ b/src/protagonist/Orchestrator.Tests/Integration/ManifestHandlingTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; @@ -8,6 +9,7 @@ using IIIF.ImageApi.V2; using IIIF.ImageApi.V3; using IIIF.Presentation.V3.Annotation; +using IIIF.Presentation.V3.Strings; using IIIF.Serialisation; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; @@ -213,7 +215,86 @@ public async Task Get_ManifestForImage_ReturnsManifest_ByName() response.Headers.CacheControl.Public.Should().BeTrue(); response.Headers.CacheControl.MaxAge.Should().BeGreaterThan(TimeSpan.FromSeconds(2)); } + + [Fact] + public async Task Get_V3ManifestForImage_ReturnsManifest_WithCustomFields() + { + // Arrange + const string defaultLanguage = "none"; + var id = AssetId.FromString($"99/1/{nameof(Get_V3ManifestForImage_ReturnsManifest_WithCustomFields)}"); + var namedId = $"test/1/{nameof(Get_V3ManifestForImage_ReturnsManifest_WithCustomFields)}"; + var asset = await dbFixture.DbContext.Images.AddTestAsset(id, + origin: "testorigin", + ref1: "string-example-1", + ref2: "string-example-2", + ref3: "string-example-3", + num1: 1, + num2: 2, + num3: 3); + await dbFixture.DbContext.SaveChangesAsync(); + + var path = $"iiif-manifest/{namedId}"; + + // Act + var response = await httpClient.GetAsync(path); + + // Assert + var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync()); + var metadata = jsonResponse.SelectToken("items[0].metadata") + .ToObject>() + .ToDictionary( + lvp => lvp.Label[defaultLanguage][0], + lvp => lvp.Value[defaultLanguage][0]); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + metadata.Should().NotBeNullOrEmpty(); + metadata.Should().Contain("String 1", asset.Entity.Reference1); + metadata.Should().Contain("String 2", asset.Entity.Reference2); + metadata.Should().Contain("String 3", asset.Entity.Reference3); + metadata.Should().Contain("Number 1", asset.Entity.NumberReference1.ToString()); + metadata.Should().Contain("Number 2", asset.Entity.NumberReference2.ToString()); + metadata.Should().Contain("Number 3", asset.Entity.NumberReference3.ToString()); + } + + [Fact] + public async Task Get_V2ManifestForImage_ReturnsManifest_WithCustomFields() + { + // Arrange + var id = AssetId.FromString($"99/1/{nameof(Get_V2ManifestForImage_ReturnsManifest_WithCustomFields)}"); + var namedId = $"test/1/{nameof(Get_V2ManifestForImage_ReturnsManifest_WithCustomFields)}"; + var asset = await dbFixture.DbContext.Images.AddTestAsset(id, + origin: "testorigin", + ref1: "string-example-1", + ref2: "string-example-2", + ref3: "string-example-3", + num1: 1, + num2: 2, + num3: 3); + await dbFixture.DbContext.SaveChangesAsync(); + + var path = $"iiif-manifest/v2/{namedId}"; + + // Act + var response = await httpClient.GetAsync(path); + + // Assert + var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync()); + var metadata = jsonResponse.SelectToken("sequences[0].canvases[0].metadata") + .ToObject>() + .ToDictionary( + m => m.Label.LanguageValues[0].Value, + m => m.Value.LanguageValues[0].Value); + response.StatusCode.Should().Be(HttpStatusCode.OK); + metadata.Should().NotBeNullOrEmpty(); + metadata.Should().Contain("String 1", asset.Entity.Reference1); + metadata.Should().Contain("String 2", asset.Entity.Reference2); + metadata.Should().Contain("String 3", asset.Entity.Reference3); + metadata.Should().Contain("Number 1", asset.Entity.NumberReference1.ToString()); + metadata.Should().Contain("Number 2", asset.Entity.NumberReference2.ToString()); + metadata.Should().Contain("Number 3", asset.Entity.NumberReference3.ToString()); + } + [Fact] public async Task Get_V2ManifestForRestrictedImage_ReturnsManifest_WithoutAuthServices() { @@ -231,11 +312,14 @@ await dbFixture.DbContext.Images.AddTestAsset(id, roles: "clickthrough", maxUnau // Assert var jsonContent = await response.Content.ReadAsStringAsync(); var jsonResponse = JObject.Parse(jsonContent); + jsonResponse["@id"].ToString().Should().Be($"http://localhost/iiif-manifest/v2/{id}"); jsonResponse.SelectToken("sequences[0].canvases[0].thumbnail.@id").Value() .Should().StartWith($"http://localhost/thumbs/{id}/full"); - - jsonContent.Should().NotContain("clickthrough", "auth services are not included in v2 manifests"); + jsonResponse.SelectTokens("sequences[*].canvases[*].images[*].resource.service") + .Select(token => token.ToString()) + .Should().NotContainMatch("*clickthrough*", "auth services are not included in v2 manifests"); + response.StatusCode.Should().Be(HttpStatusCode.OK); response.Headers.Should().ContainKey("x-asset-id").WhoseValue.Should().ContainSingle(id.ToString()); response.Headers.CacheControl.Public.Should().BeTrue(); diff --git a/src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs b/src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs index afaa8d01d..6f054fe71 100644 --- a/src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs +++ b/src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs @@ -266,13 +266,16 @@ public async Task Get_AssetsRequireAuth_ReturnsV2ManifestWithoutAuthServices() // Act var response = await httpClient.GetAsync(path); + var test = response.Content.ReadAsStringAsync(); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var jsonContent = await response.Content.ReadAsStringAsync(); var jsonResponse = JObject.Parse(jsonContent); - jsonContent.Should().NotContain("clickthrough", "auth services are not included in v2 manifests"); + jsonResponse.SelectTokens("sequences[*].canvases[*].images[*].resource.service") + .Select(token => token.ToString()) + .Should().NotContainMatch("*clickthrough*", "auth services are not included in v2 manifests"); jsonResponse.SelectToken("sequences[0].canvases").Count().Should().Be(3); } diff --git a/src/protagonist/Orchestrator/Infrastructure/IIIF/IIIFCanvasFactory.cs b/src/protagonist/Orchestrator/Infrastructure/IIIF/IIIFCanvasFactory.cs index 6353781bf..a632cca50 100644 --- a/src/protagonist/Orchestrator/Infrastructure/IIIF/IIIFCanvasFactory.cs +++ b/src/protagonist/Orchestrator/Infrastructure/IIIF/IIIFCanvasFactory.cs @@ -36,7 +36,8 @@ public class IIIFCanvasFactory private readonly IPolicyRepository policyRepository; private readonly OrchestratorSettings orchestratorSettings; private readonly Dictionary thumbnailPolicies = new(); - + private const string MetadataLanguage = "none"; + public IIIFCanvasFactory( IAssetPathGenerator assetPathGenerator, IOptions orchestratorSettings, @@ -67,6 +68,11 @@ public IIIFCanvasFactory( Label = new LanguageMap("en", $"Canvas {counter}"), Width = i.Width, Height = i.Height, + Metadata = GetImageMetadata(i) + .Select(m => + new LabelValuePair(new LanguageMap(MetadataLanguage, m.Key), + new LanguageMap(MetadataLanguage, m.Value))) + .ToList(), Items = new AnnotationPage { Id = $"{canvasId}/page", @@ -124,6 +130,13 @@ public IIIFCanvasFactory( Label = new MetaDataValue($"Canvas {counter}"), Width = i.Width, Height = i.Height, + Metadata = GetImageMetadata(i) + .Select(m => new IIIF2.Metadata() + { + Label = new MetaDataValue(m.Key), + Value = new MetaDataValue(m.Value) + }) + .ToList(), Images = new ImageAnnotation { Id = string.Concat(fullyQualifiedImageId, "/imageanno/0"), @@ -135,7 +148,7 @@ public IIIFCanvasFactory( Width = thumbnailSizes.MaxDerivativeSize.Width, Height = thumbnailSizes.MaxDerivativeSize.Height, Service = GetImageServices(i, customerPathElement, null) - } + }, }.AsList() }; @@ -313,7 +326,22 @@ private List GetImageServices(Asset asset, CustomerPathElement custome return authServiceToAdd.AsListOf(); } } - + + private Dictionary GetImageMetadata(Asset asset) + { + return new Dictionary() + { + { "String 1", asset.Reference1 ?? string.Empty }, + { "String 2", asset.Reference2 ?? string.Empty }, + { "String 3", asset.Reference3 ?? string.Empty }, + { "Number 1", (asset.NumberReference1 ?? 0).ToString() }, + { "Number 2", (asset.NumberReference2 ?? 0).ToString() }, + { "Number 3", (asset.NumberReference3 ?? 0).ToString() }, + { "Tags", asset.Tags ?? string.Empty }, + { "Roles", asset.Roles ?? string.Empty } + }; + } + /// /// Class containing details of available thumbnail sizes ///