diff --git a/src/protagonist/Orchestrator.Tests/Integration/ManifestHandlingTests.cs b/src/protagonist/Orchestrator.Tests/Integration/ManifestHandlingTests.cs index 5fcfdb5db..815deac90 100644 --- a/src/protagonist/Orchestrator.Tests/Integration/ManifestHandlingTests.cs +++ b/src/protagonist/Orchestrator.Tests/Integration/ManifestHandlingTests.cs @@ -16,6 +16,7 @@ using Newtonsoft.Json.Linq; using Orchestrator.Infrastructure.IIIF; using Orchestrator.Tests.Integration.Infrastructure; +using Test.Helpers.Data; using Test.Helpers.Integration; using IIIF3 = IIIF.Presentation.V3; @@ -125,7 +126,7 @@ public async Task Get_UnknownImage_Returns404(string path) public async Task Get_NotForDelivery_Returns404() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_NotForDelivery_Returns404)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, notForDelivery: true); await dbFixture.DbContext.SaveChangesAsync(); @@ -144,7 +145,7 @@ public async Task Get_NotForDelivery_Returns404() public async Task Get_NonImage_Returns404(AssetFamily family) { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_NonImage_Returns404)}:{family}"); + var id = AssetIdGenerator.GetAssetId(assetPostfix: $":{family}"); await dbFixture.DbContext.Images.AddTestAsset(id, family: family); await dbFixture.DbContext.SaveChangesAsync(); @@ -161,7 +162,7 @@ public async Task Get_NonImage_Returns404(AssetFamily family) public async Task Get_ManifestForImage_ReturnsManifest_CustomPathRules_Ignored() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ManifestForImage_ReturnsManifest_CustomPathRules_Ignored)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); @@ -184,11 +185,38 @@ public async Task Get_ManifestForImage_ReturnsManifest_CustomPathRules_Ignored() response.Headers.CacheControl.MaxAge.Should().BeGreaterThan(TimeSpan.FromSeconds(2)); } + [Fact] + public async Task Get_ManifestForImage_ReturnsManifest_IdIgnoresQueryString() + { + // Arrange + var id = AssetIdGenerator.GetAssetId(); + await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); + await dbFixture.DbContext.SaveChangesAsync(); + + var path = $"iiif-manifest/v2/{id}"; + + // Act + var request = new HttpRequestMessage(HttpMethod.Get, $"{path}?foo=bar"); + request.Headers.Add("Host", "my-proxy.com"); + var response = await httpClient.SendAsync(request); + + // Assert + var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync()); + jsonResponse["@id"].ToString().Should().Be($"http://my-proxy.com/iiif-manifest/v2/{id}"); + jsonResponse.SelectToken("sequences[0].canvases[0].thumbnail.@id").Value() + .Should().StartWith($"http://my-proxy.com/thumbs/{id}/full"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + response.Headers.Should().ContainKey("x-asset-id").WhoseValue.Should().ContainSingle(id.ToString()); + response.Headers.CacheControl.Public.Should().BeTrue(); + response.Headers.CacheControl.MaxAge.Should().BeGreaterThan(TimeSpan.FromSeconds(2)); + } + [Fact] public async Task Get_ManifestForImage_ReturnsManifest() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ManifestForImage_ReturnsManifest)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); @@ -213,7 +241,7 @@ public async Task Get_ManifestForImage_ReturnsManifest() public async Task Get_V2ManifestForImage_ReturnsManifest_FromMetadata() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ManifestForImage_ReturnsManifest)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels) .WithTestThumbnailMetadata(); await dbFixture.DbContext.SaveChangesAsync(); @@ -239,7 +267,7 @@ await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDel public async Task Get_V2ManifestForImage_ReturnsManifestNoThumbnails_WhenNoThumbsChannel() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ManifestForImage_ReturnsManifest)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin").WithTestThumbnailMetadata(); await dbFixture.DbContext.SaveChangesAsync(); @@ -263,7 +291,7 @@ public async Task Get_V2ManifestForImage_ReturnsManifestNoThumbnails_WhenNoThumb public async Task Get_ManifestForImage_ReturnsManifest_ByName() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ManifestForImage_ReturnsManifest_ByName)}"); + var id = AssetIdGenerator.GetAssetId(); var namedId = $"test/1/{nameof(Get_ManifestForImage_ReturnsManifest_ByName)}"; await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); @@ -290,7 +318,7 @@ public async Task Get_V3ManifestForImage_ReturnsManifest_WithCustomFields() { // Arrange const string defaultLanguage = "none"; - var id = AssetId.FromString($"99/1/{nameof(Get_V3ManifestForImage_ReturnsManifest_WithCustomFields)}"); + var id = AssetIdGenerator.GetAssetId(); var namedId = $"test/1/{nameof(Get_V3ManifestForImage_ReturnsManifest_WithCustomFields)}"; var asset = await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", @@ -330,7 +358,7 @@ public async Task Get_V3ManifestForImage_ReturnsManifest_WithCustomFields() public async Task Get_V3ManifestForImage_ReturnsManifest_FromMetadata() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ManifestForImage_ReturnsManifest)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels) .WithTestThumbnailMetadata(); await dbFixture.DbContext.SaveChangesAsync(); @@ -356,7 +384,7 @@ await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDel public async Task Get_V3ManifestForImage_ReturnsManifestNoThumbnails_WhenNoThumbsChannel() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ManifestForImage_ReturnsManifest)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin").WithTestThumbnailMetadata(); await dbFixture.DbContext.SaveChangesAsync(); @@ -380,7 +408,7 @@ public async Task Get_V3ManifestForImage_ReturnsManifestNoThumbnails_WhenNoThumb public async Task Get_V2ManifestForImage_ReturnsManifest_WithCustomFields() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_V2ManifestForImage_ReturnsManifest_WithCustomFields)}"); + var id = AssetIdGenerator.GetAssetId(); var namedId = $"test/1/{nameof(Get_V2ManifestForImage_ReturnsManifest_WithCustomFields)}"; var asset = await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", @@ -420,7 +448,7 @@ public async Task Get_V2ManifestForImage_ReturnsManifest_WithCustomFields() public async Task Get_V2ManifestForRestrictedImage_ReturnsManifest_WithoutAuthServices() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_V2ManifestForRestrictedImage_ReturnsManifest_WithoutAuthServices)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, roles: "clickthrough", maxUnauthorised: 400, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); @@ -451,7 +479,7 @@ public async Task Get_V2ManifestForRestrictedImage_ReturnsManifest_WithoutAuthSe public async Task Get_ReturnsV2Manifest_ViaConneg() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ReturnsV2Manifest_ViaConneg)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); var path = $"iiif-manifest/{id}"; @@ -478,7 +506,7 @@ public async Task Get_ReturnsV2Manifest_ViaConneg() public async Task Get_ReturnsV2Manifest_ViaDirectPath() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ReturnsV2Manifest_ViaDirectPath)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); var path = $"iiif-manifest/v2/{id}"; @@ -502,7 +530,7 @@ public async Task Get_ReturnsV2Manifest_ViaDirectPath() public async Task Get_ReturnsV3Manifest_ViaConneg() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ReturnsV3Manifest_ViaConneg)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); var path = $"iiif-manifest/{id}"; @@ -529,7 +557,7 @@ public async Task Get_ReturnsV3Manifest_ViaConneg() public async Task Get_ReturnsV3Manifest_ViaDirectPath() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ReturnsV3Manifest_ViaDirectPath)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); var path = $"iiif-manifest/{id}"; @@ -553,7 +581,7 @@ public async Task Get_ReturnsV3Manifest_ViaDirectPath() public async Task Get_ReturnsV3ManifestWithCorrectItemCount_AsCanonical() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ReturnsV3ManifestWithCorrectItemCount_AsCanonical)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); var path = $"iiif-manifest/{id}"; @@ -576,7 +604,7 @@ public async Task Get_ReturnsV3ManifestWithCorrectItemCount_AsCanonical() public async Task Get_ReturnsMultipleImageServices() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_ReturnsMultipleImageServices)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); var path = $"iiif-manifest/{id}"; @@ -603,7 +631,7 @@ public async Task Get_ReturnsMultipleImageServices() public async Task Get_V3ManifestForRestrictedImage_ReturnsManifest_WithAuthServices() { // Arrange - var id = AssetId.FromString($"99/1/{nameof(Get_V3ManifestForRestrictedImage_ReturnsManifest_WithAuthServices)}"); + var id = AssetIdGenerator.GetAssetId(); await dbFixture.DbContext.Images.AddTestAsset(id, roles: "clickthrough", maxUnauthorised: 400, origin: "testorigin", imageDeliveryChannels: imageDeliveryChannels); await dbFixture.DbContext.SaveChangesAsync(); diff --git a/src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs b/src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs index 445f8e9e9..af446c72f 100644 --- a/src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs +++ b/src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs @@ -181,11 +181,30 @@ public async Task Get_ReturnsV2ManifestWithCorrectCount_ViaDirectPath() jsonResponse.SelectToken("sequences[0].canvases").Count().Should().Be(3); } + [Fact] + public async Task Get_ReturnsV2Manifest_WithCorrectId_IgnoringQueryParam() + { + // Arrange + const string path = "iiif-resource/v2/99/test-named-query/my-ref/1"; + const string iiif2 = "application/ld+json; profile=\"http://iiif.io/api/presentation/2/context.json\""; + + // Act + var response = await httpClient.GetAsync($"{path}?foo=bar"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + response.Headers.Vary.Should().Contain("Accept"); + response.Content.Headers.ContentType.ToString().Should().Be(iiif2); + var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync()); + jsonResponse["@id"].ToString().Should().Be($"http://localhost/{path}"); + } + [Fact] public async Task Get_ReturnsV3ManifestWithCorrectCount_ViaConneg() { // Arrange const string path = "iiif-resource/99/test-named-query/my-ref/1"; + const string iiif2 = "application/ld+json; profile=\"http://iiif.io/api/presentation/3/context.json\""; // Act @@ -219,6 +238,24 @@ public async Task Get_ReturnsV3ManifestWithCorrectCount_ViaDirectPath() jsonResponse.SelectToken("items").Count().Should().Be(3); } + [Fact] + public async Task Get_ReturnsV3Manifest_WithCorrectId_IgnoringQueryParam() + { + // Arrange + const string path = "iiif-resource/v3/99/test-named-query/my-ref/1"; + const string iiif3 = "application/ld+json; profile=\"http://iiif.io/api/presentation/3/context.json\""; + + // Act + var response = await httpClient.GetAsync($"{path}?foo=bar"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + response.Headers.Vary.Should().Contain("Accept"); + response.Content.Headers.ContentType.ToString().Should().Be(iiif3); + var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync()); + jsonResponse["id"].ToString().Should().Be($"http://localhost/{path}"); + } + [Fact] public async Task Get_ReturnsV3ManifestWithCorrectCount_AsCanonical() { diff --git a/src/protagonist/Orchestrator/Features/Manifests/IIIFNamedQueryProjector.cs b/src/protagonist/Orchestrator/Features/Manifests/IIIFNamedQueryProjector.cs index 966bf5dc1..5f2ee0a05 100644 --- a/src/protagonist/Orchestrator/Features/Manifests/IIIFNamedQueryProjector.cs +++ b/src/protagonist/Orchestrator/Features/Manifests/IIIFNamedQueryProjector.cs @@ -57,7 +57,7 @@ public IIIFNamedQueryProjector(IIIFManifestBuilder manifestBuilder) CancellationToken cancellationToken) { var sequenceRootUrl = request.GetDisplayUrl("/iiif-query/"); - var manifestId = request.GetDisplayUrl(); + var manifestId = GetManifestId(request); var label = GetManifestLabel(parsedNamedQuery); var manifest = await manifestBuilder.GenerateV2Manifest(results, customerPathElement, manifestId, label, sequenceRootUrl, @@ -70,7 +70,7 @@ public IIIFNamedQueryProjector(IIIFManifestBuilder manifestBuilder) CustomerPathElement customerPathElement, List results, HttpRequest request, CancellationToken cancellationToken) { - var manifestId = request.GetDisplayUrl(); + var manifestId = GetManifestId(request); var label = GetManifestLabel(parsedNamedQuery); var manifest = await manifestBuilder.GenerateV3Manifest(results, customerPathElement, manifestId, label, @@ -79,6 +79,9 @@ public IIIFNamedQueryProjector(IIIFManifestBuilder manifestBuilder) return manifest; } + private static string GetManifestId(HttpRequest request) => + request.GetDisplayUrl(request.Path.Value, includeQueryParams: false); + private static string GetManifestLabel(IIIFParsedNamedQuery parsedNamedQuery) => $"Generated from '{parsedNamedQuery.NamedQueryName}' named query"; } \ No newline at end of file diff --git a/src/protagonist/Orchestrator/Features/Manifests/Requests/GetManifestForAsset.cs b/src/protagonist/Orchestrator/Features/Manifests/Requests/GetManifestForAsset.cs index 2e122d2c1..302daeb7f 100644 --- a/src/protagonist/Orchestrator/Features/Manifests/Requests/GetManifestForAsset.cs +++ b/src/protagonist/Orchestrator/Features/Manifests/Requests/GetManifestForAsset.cs @@ -101,5 +101,5 @@ public class GetManifestForAssetHandler : IRequestHandler assetPathGenerator.GetFullPathForRequest(baseAssetRequest, true); + => assetPathGenerator.GetFullPathForRequest(baseAssetRequest, true, false); } \ No newline at end of file diff --git a/src/protagonist/Test.Helpers/Data/AssetIdGenerator.cs b/src/protagonist/Test.Helpers/Data/AssetIdGenerator.cs index 43ab90042..7ede0304f 100644 --- a/src/protagonist/Test.Helpers/Data/AssetIdGenerator.cs +++ b/src/protagonist/Test.Helpers/Data/AssetIdGenerator.cs @@ -8,6 +8,7 @@ public static class AssetIdGenerator /// /// Generate new using calling function as "asset" part by default /// - public static AssetId GetAssetId(int customer = 99, int space = 1, [CallerMemberName] string asset = "") - => new(customer, space, asset); + public static AssetId GetAssetId(int customer = 99, int space = 1, [CallerMemberName] string asset = "", + string assetPostfix = "") + => new(customer, space, $"{asset}{assetPostfix}"); } \ No newline at end of file