diff --git a/src/IIIF/IIIF.Tests/ImageApi/ImageRequestXTests.cs b/src/IIIF/IIIF.Tests/ImageApi/ImageRequestXTests.cs index 3b2f183..9ef29d3 100644 --- a/src/IIIF/IIIF.Tests/ImageApi/ImageRequestXTests.cs +++ b/src/IIIF/IIIF.Tests/ImageApi/ImageRequestXTests.cs @@ -1,6 +1,6 @@ -using FluentAssertions; +using System; +using FluentAssertions; using IIIF.ImageApi; -using Xunit; namespace IIIF.Tests.ImageApi; @@ -221,4 +221,88 @@ public void Parse_CorrectPercentageScaled() // Assert result.Should().BeEquivalentTo(expected); } + + [Theory] + [InlineData("my-asset")] + [InlineData("my-asset/")] + public void Parse_IsBase(string path) + { + // Arrange and Act + const string prefix = "iiif-img/27/1/"; + var result = ImageRequest.Parse($"{prefix}{path}", prefix); + + // Assert + result.IsBase.Should().BeTrue(); + } + + [Fact] + public void Parse_InfoJson() + { + // Arrange and Act + const string prefix = "iiif-img/27/1/"; + var result = ImageRequest.Parse($"{prefix}my-asset/info.json", prefix); + + // Assert + result.IsInformationRequest.Should().BeTrue(); + } + + [Fact] + public void Parse_Fails_WhenInfoHasInvalidExtension() + { + // Arrange and Act + const string prefix = "iiif-img/27/1/"; + var action = () => ImageRequest.Parse($"{prefix}my-asset/info.jsonll", prefix); + + // Assert + action.Should().ThrowExactly(); + } + + [Theory] + [InlineData("iiif-img/27/1/")] + [InlineData("/iiif-img/27/1/")] + [InlineData("/iiif-img/27/1")] + [InlineData("iiif-img/27/1")] + public void Parse_Validate_HandlesPrefixFormats(string prefix) + { + // Arrange and Act + const string request = $"iiif-img/27/1/my-asset/full/800,/0/default.jpg"; + var action = () => ImageRequest.Parse(request, prefix, true); + + // Assert + action.Should().NotThrow(); + } + + [Theory] + [InlineData("my-asset//full/800,/0/default.jpg")] + [InlineData("my-asset/full//800,/0/default.jpg")] + [InlineData("my-asset/full/800,//0/default.jpg")] + [InlineData("my-asset/full/800,/0//default.jpg")] + public void Parse_Validate_Fails_WhenGivenExtraSegments(string path) + { + // Arrange and Act + const string prefix = "iiif-img/27/1/"; + var action = () => ImageRequest.Parse($"{prefix}{path}", prefix, true); + + // Assert + action.Should().ThrowExactly() + .WithMessage("Path contains empty or an invalid number of segments"); + } + + [Theory] + [InlineData("my-asset//800,/0/default.jpg")] + [InlineData("my-asset/full//0/default.jpg")] + [InlineData("my-asset/full/800,//default.jpg")] + [InlineData("my-asset/full/800,/0/")] + [InlineData("my-asset////default.jpg")] + [InlineData("my-asset////")] + public void Parse_Validate_Fails_WhenGivenEmptyParameters(string path) + { + // Arrange and Act + const string prefix = "iiif-img/27/1/"; + var action = () => ImageRequest.Parse($"{prefix}{path}", prefix, true); + + // Assert + action.Should().ThrowExactly() + .WithMessage("Path contains empty or an invalid number of segments"); + } } \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/ImageRequest.cs b/src/IIIF/IIIF/ImageApi/ImageRequest.cs index ce22863..4d54eb5 100644 --- a/src/IIIF/IIIF/ImageApi/ImageRequest.cs +++ b/src/IIIF/IIIF/ImageApi/ImageRequest.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; namespace IIIF.ImageApi; @@ -25,7 +26,15 @@ public class ImageRequest /// public string ImageRequestPath => OriginalPath.Replace(Identifier, string.Empty); - public static ImageRequest Parse(string path, string prefix) + /// + /// Parses an image request path as a IIIF ImageRequest object + /// + /// A ImageRequest object + /// The image request path + /// The image request prefix + /// If true, throws an ArgumentException if the image request contains empty values, + /// or an invalid number of segments + public static ImageRequest Parse(string path, string prefix, bool validateSegments = false) { if (path[0] == '/') path = path[1..]; @@ -35,12 +44,16 @@ public static ImageRequest Parse(string path, string prefix) if (prefix != path[..prefix.Length]) throw new ArgumentException("Path does not start with prefix", nameof(prefix)); path = path[prefix.Length..]; + if (path[0] == '/') path = path[1..]; } var request = new ImageRequest { Prefix = prefix }; + var parts = path.Split('/'); + request.Identifier = parts[0]; - if (parts.Length == 1 || parts[1] == string.Empty) + + if (parts.Length == 1 || (parts.Length == 2 && parts[1] == string.Empty)) { // likely the server will want to redirect this request.IsBase = true; @@ -52,6 +65,11 @@ public static ImageRequest Parse(string path, string prefix) request.IsInformationRequest = true; return request; } + + if (validateSegments && (parts.Length != 5 || parts.Any(string.IsNullOrEmpty))) + { + throw new ArgumentException("Path contains empty or an invalid number of segments"); + } request.OriginalPath = path; request.Region = RegionParameter.Parse(parts[1]);