From bb156aa64e8828f25f1100438df565321672e0b6 Mon Sep 17 00:00:00 2001 From: Donald Gray Date: Thu, 12 May 2022 11:08:56 +0100 Subject: [PATCH 1/4] Remove ImageService2Reference and ImageService3Reference ImageService2 and ImageService3 can be used in their place --- src/IIIF/IIIF/ImageApi/Service/ImageService3.cs | 2 +- .../IIIF/Presentation/V2/ImageService2Reference.cs | 14 -------------- .../IIIF/Presentation/V3/ImageService3Reference.cs | 9 --------- 3 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 src/IIIF/IIIF/Presentation/V2/ImageService2Reference.cs delete mode 100644 src/IIIF/IIIF/Presentation/V3/ImageService3Reference.cs diff --git a/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs b/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs index 87be1cd..b4e8010 100644 --- a/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs +++ b/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs @@ -4,7 +4,7 @@ namespace IIIF.ImageApi.Service { - public class ImageService3 : ResourceBase, IService + public class ImageService3 : ResourceBase { public const string Image3Context = "http://iiif.io/api/image/3/context.json"; public const string ImageProtocol = "http://iiif.io/api/image"; diff --git a/src/IIIF/IIIF/Presentation/V2/ImageService2Reference.cs b/src/IIIF/IIIF/Presentation/V2/ImageService2Reference.cs deleted file mode 100644 index 7ea4486..0000000 --- a/src/IIIF/IIIF/Presentation/V2/ImageService2Reference.cs +++ /dev/null @@ -1,14 +0,0 @@ -using IIIF.ImageApi.Service; -using System; - -namespace IIIF.Presentation.V2 -{ - public class ImageService2Reference : ResourceBase, IService - { - public override string Type - { - get => nameof(ImageService2); - set => throw new NotImplementedException(); - } - } -} diff --git a/src/IIIF/IIIF/Presentation/V3/ImageService3Reference.cs b/src/IIIF/IIIF/Presentation/V3/ImageService3Reference.cs deleted file mode 100644 index affb527..0000000 --- a/src/IIIF/IIIF/Presentation/V3/ImageService3Reference.cs +++ /dev/null @@ -1,9 +0,0 @@ -using IIIF.ImageApi.Service; - -namespace IIIF.Presentation.V3 -{ - public class ImageService3Reference : ResourceBase, IService - { - public override string Type => nameof(ImageService3); - } -} From 7ba7c906b40d86445a89a6e888f49e3d6558ed62 Mon Sep 17 00:00:00 2001 From: Donald Gray Date: Thu, 12 May 2022 11:46:21 +0100 Subject: [PATCH 2/4] Allow canvas to have items that target itself --- .../IIIF.Tests/Presentation/V3/CanvasTests.cs | 72 ++++++++++++++++++- .../IIIF/Serialisation/IIIFSerialiserX.cs | 1 + 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/IIIF/IIIF.Tests/Presentation/V3/CanvasTests.cs b/src/IIIF/IIIF.Tests/Presentation/V3/CanvasTests.cs index 1d47c57..2b7e4e9 100644 --- a/src/IIIF/IIIF.Tests/Presentation/V3/CanvasTests.cs +++ b/src/IIIF/IIIF.Tests/Presentation/V3/CanvasTests.cs @@ -2,6 +2,7 @@ using FluentAssertions; using IIIF.Presentation.V3; using IIIF.Presentation.V3.Annotation; +using IIIF.Presentation.V3.Content; using IIIF.Presentation.V3.Strings; using IIIF.Serialisation; using Xunit; @@ -85,7 +86,7 @@ public void SerialiseTargetAsId_True_RendersIdAsTarget() [Fact] public void SerialiseTargetAsId_False_RendersFullCanvasAsTarget() { - var targetIsIdOnlyCanvas = new Canvas + var targetIsFullCanvas = new Canvas { Id = "https://test.example.com/canvas/full", Width = 1000, @@ -102,7 +103,7 @@ public void SerialiseTargetAsId_False_RendersFullCanvasAsTarget() Id = "https://test.example.com/canvas/referencing/page", Items = new List { - new PaintingAnnotation { Target = targetIsIdOnlyCanvas, } + new PaintingAnnotation { Target = targetIsFullCanvas, } } } } @@ -113,7 +114,7 @@ public void SerialiseTargetAsId_False_RendersFullCanvasAsTarget() Context = "http://iiif.io/api/presentation/3/context.json", Id = "https://test.example.com/manifest", Label = new LanguageMap("en", "Test string"), - Items = new List { targetIsIdOnlyCanvas, referencingCanvas } + Items = new List { targetIsFullCanvas, referencingCanvas } }; var serialisedManifest = manifest.AsJson().Replace("\r\n", "\n"); @@ -157,5 +158,70 @@ public void SerialiseTargetAsId_False_RendersFullCanvasAsTarget() serialisedManifest.Should().BeEquivalentTo(expected); } + + [Fact] + public void CanSelfReferenceCanvas() + { + var canvas = new Canvas + { + Id = "https://test.example.com/canvas/target-id-only", + SerialiseTargetAsId = true + }; + + canvas.Items = new List + { + new() + { + Id = "https://test.example.com/canvas/referencing/page", + Items = new List + { + new PaintingAnnotation + { + Id = "https://test.example.com/canvas/referencing/page/anno", + Target = canvas, + } + } + } + }; + + var manifest = new Manifest + { + Context = "http://iiif.io/api/presentation/3/context.json", + Id = "https://test.example.com/manifest", + Label = new LanguageMap("en", "Test string"), + Items = new List { canvas } + }; + + var serialisedManifest = manifest.AsJson().Replace("\r\n", "\n"); + + const string expected = @"{ + ""@context"": ""http://iiif.io/api/presentation/3/context.json"", + ""id"": ""https://test.example.com/manifest"", + ""type"": ""Manifest"", + ""label"": {""en"":[""Test string""]}, + ""items"": [ + { + ""id"": ""https://test.example.com/canvas/target-id-only"", + ""type"": ""Canvas"", + ""items"": [ + { + ""id"": ""https://test.example.com/canvas/referencing/page"", + ""type"": ""AnnotationPage"", + ""items"": [ + { + ""id"": ""https://test.example.com/canvas/referencing/page/anno"", + ""type"": ""Annotation"", + ""motivation"": ""painting"", + ""target"": ""https://test.example.com/canvas/target-id-only"" + } + ] + } + ] + } + ] +}"; + + serialisedManifest.Should().BeEquivalentTo(expected); + } } } \ No newline at end of file diff --git a/src/IIIF/IIIF/Serialisation/IIIFSerialiserX.cs b/src/IIIF/IIIF/Serialisation/IIIFSerialiserX.cs index 54e5eb2..305be23 100644 --- a/src/IIIF/IIIF/Serialisation/IIIFSerialiserX.cs +++ b/src/IIIF/IIIF/Serialisation/IIIFSerialiserX.cs @@ -14,6 +14,7 @@ public static class IIIFSerialiserX NullValueHandling = NullValueHandling.Ignore, ContractResolver = new PrettyIIIFContractResolver(), Formatting = Formatting.Indented, + ReferenceLoopHandling = ReferenceLoopHandling.Serialize, Converters = new List { new SizeConverter(), new StringArrayConverter(), new ServiceReferenceConverter(), From af11605cb67e480a604c5cc7a3b7f7eefe98867a Mon Sep 17 00:00:00 2001 From: Donald Gray Date: Thu, 12 May 2022 11:48:10 +0100 Subject: [PATCH 3/4] Add imageService3 serialisation tests --- .../ImageService3SerialiserTests.cs | 38 +++++++++++++++++++ .../IIIF/ImageApi/Service/ImageService3.cs | 9 +---- .../ServiceReferenceConverter.cs | 3 +- src/IIIF/IIIF/Serialisation/SizeConverter.cs | 3 +- .../IIIF/Serialisation/TargetConverter.cs | 1 - 5 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 src/IIIF/IIIF.Tests/Serialisation/ImageService3SerialiserTests.cs diff --git a/src/IIIF/IIIF.Tests/Serialisation/ImageService3SerialiserTests.cs b/src/IIIF/IIIF.Tests/Serialisation/ImageService3SerialiserTests.cs new file mode 100644 index 0000000..004c6e7 --- /dev/null +++ b/src/IIIF/IIIF.Tests/Serialisation/ImageService3SerialiserTests.cs @@ -0,0 +1,38 @@ +using FluentAssertions; +using IIIF.ImageApi.Service; +using IIIF.Serialisation; +using Xunit; + +namespace IIIF.Tests.Serialisation +{ + public class ImageService3SerialiserTests + { + [Fact] + public void WriteJson_OutputsExpected_IfNoProfileOrProfileDescription() + { + // Arrange + var imageService = new ImageService3 { Id = "foo" }; + const string expected = "{\n \"id\": \"foo\",\n \"type\": \"ImageService3\"\n}"; + + // Act + var result = imageService.AsJson().Replace("\r\n", "\n"); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void WriteJson_OutputsExpected_ProfileOnly() + { + // Arrange + var imageService = new ImageService3 { Id = "foo", Profile = "bar" }; + const string expected = "{\n \"id\": \"foo\",\n \"type\": \"ImageService3\",\n \"profile\": \"bar\"\n}"; + + // Act + var result = imageService.AsJson().Replace("\r\n", "\n"); + + // Assert + result.Should().Be(expected); + } + } +} \ No newline at end of file diff --git a/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs b/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs index b4e8010..7bd473f 100644 --- a/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs +++ b/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs @@ -9,15 +9,10 @@ public class ImageService3 : ResourceBase public const string Image3Context = "http://iiif.io/api/image/3/context.json"; public const string ImageProtocol = "http://iiif.io/api/image"; - public ImageService3() - { - Context = Image3Context; - } - public override string Type => nameof(ImageService3); - [JsonProperty(Order = 3)] - public string Protocol => ImageProtocol; + [JsonProperty(Order = 10)] + public string Protocol { get; set; } [JsonProperty(Order = 11)] public int Width { get; set; } diff --git a/src/IIIF/IIIF/Serialisation/ServiceReferenceConverter.cs b/src/IIIF/IIIF/Serialisation/ServiceReferenceConverter.cs index 36a0e31..f03bda4 100644 --- a/src/IIIF/IIIF/Serialisation/ServiceReferenceConverter.cs +++ b/src/IIIF/IIIF/Serialisation/ServiceReferenceConverter.cs @@ -1,5 +1,4 @@ -using IIIF.Presentation.V2.Serialisation; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace IIIF.Serialisation { diff --git a/src/IIIF/IIIF/Serialisation/SizeConverter.cs b/src/IIIF/IIIF/Serialisation/SizeConverter.cs index abf5a2c..fcda0c1 100644 --- a/src/IIIF/IIIF/Serialisation/SizeConverter.cs +++ b/src/IIIF/IIIF/Serialisation/SizeConverter.cs @@ -1,5 +1,4 @@ -using IIIF.Presentation.V2.Serialisation; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace IIIF.Serialisation { diff --git a/src/IIIF/IIIF/Serialisation/TargetConverter.cs b/src/IIIF/IIIF/Serialisation/TargetConverter.cs index 8489503..067431d 100644 --- a/src/IIIF/IIIF/Serialisation/TargetConverter.cs +++ b/src/IIIF/IIIF/Serialisation/TargetConverter.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using IIIF.Presentation.V3; using IIIF.Utils; using Newtonsoft.Json; From 30517769c39a5586f6b9a358a0a55ccbaaa3977e Mon Sep 17 00:00:00 2001 From: Donald Gray Date: Thu, 12 May 2022 11:55:15 +0100 Subject: [PATCH 4/4] ImageService3 deserialisation --- .../ManifestSerialisationTests.cs | 8 ++++++ .../IIIF/ImageApi/Service/ImageService3.cs | 3 +++ .../Deserialisation/ServiceConverter.cs | 27 +++++++++++++++---- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/IIIF/IIIF.Tests/Serialisation/ManifestSerialisationTests.cs b/src/IIIF/IIIF.Tests/Serialisation/ManifestSerialisationTests.cs index 70dbebb..7344dd3 100644 --- a/src/IIIF/IIIF.Tests/Serialisation/ManifestSerialisationTests.cs +++ b/src/IIIF/IIIF.Tests/Serialisation/ManifestSerialisationTests.cs @@ -72,6 +72,14 @@ public void CanDeserialiseSerialisedManifest() Context = ImageService2.Image2Context, Width = 1000, Height = 1001 + }, + new ImageService3 + { + Id = "https://test.example.com/canvas/1/image/3", + Profile = ImageService3.Level2Profile, + Context = ImageService3.Image3Context, + Width = 1000, + Height = 1001 } }, } diff --git a/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs b/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs index 7bd473f..9e64332 100644 --- a/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs +++ b/src/IIIF/IIIF/ImageApi/Service/ImageService3.cs @@ -7,6 +7,9 @@ namespace IIIF.ImageApi.Service public class ImageService3 : ResourceBase { public const string Image3Context = "http://iiif.io/api/image/3/context.json"; + public const string Level0Profile = "level0"; + public const string Level1Profile = "level1"; + public const string Level2Profile = "level2"; public const string ImageProtocol = "http://iiif.io/api/image"; public override string Type => nameof(ImageService3); diff --git a/src/IIIF/IIIF/Serialisation/Deserialisation/ServiceConverter.cs b/src/IIIF/IIIF/Serialisation/Deserialisation/ServiceConverter.cs index 32774d6..057700b 100644 --- a/src/IIIF/IIIF/Serialisation/Deserialisation/ServiceConverter.cs +++ b/src/IIIF/IIIF/Serialisation/Deserialisation/ServiceConverter.cs @@ -39,12 +39,29 @@ public class ServiceConverter : ReadOnlyConverter if (service == null) { - service = jsonObject["@type"].Value() switch + var atType = jsonObject["@type"]; + if (atType != null) { - "SearchService1" => new Search.V1.SearchService(), - nameof(ImageApi.Service.ImageService2) => new ImageApi.Service.ImageService2(), - _ => null - }; + service = atType.Value() switch + { + "SearchService1" => new Search.V1.SearchService(), + nameof(ImageApi.Service.ImageService2) => new ImageApi.Service.ImageService2(), + _ => null + }; + } + } + + if (service == null) + { + var type = jsonObject["type"]; + if (type != null) + { + service = type.Value() switch + { + nameof(ImageApi.Service.ImageService3) => new ImageApi.Service.ImageService3(), + _ => null + }; + } } // TODO handle ResourceBase items?