From 02cfa0f865d929dc2c1ca007abc37774e49d058b Mon Sep 17 00:00:00 2001 From: "jack.lewis" Date: Mon, 13 Jan 2025 17:17:20 +0000 Subject: [PATCH 1/2] Fixing serialization error to provide a nicer error --- .../API/Features/Manifest/ManifestController.cs | 14 ++++++++------ .../API/Features/Storage/CollectionController.cs | 3 ++- .../API/Features/Storage/Helpers/RequestBodyX.cs | 14 ++++++++------ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/IIIFPresentation/API/Features/Manifest/ManifestController.cs b/src/IIIFPresentation/API/Features/Manifest/ManifestController.cs index 562e3d66..a13ffef2 100644 --- a/src/IIIFPresentation/API/Features/Manifest/ManifestController.cs +++ b/src/IIIFPresentation/API/Features/Manifest/ManifestController.cs @@ -3,6 +3,7 @@ using API.Auth; using API.Features.Manifest.Requests; using API.Features.Manifest.Validators; +using API.Features.Storage.Helpers; using API.Infrastructure; using API.Infrastructure.Helpers; using API.Infrastructure.Requests; @@ -12,6 +13,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using Models.API.Collection; using Models.API.Manifest; namespace API.Features.Manifest; @@ -100,21 +102,21 @@ private async Task ManifestUpsert( if (!Request.HasShowExtraHeader()) return this.Forbidden(); var rawRequestBody = await Request.GetRawRequestBodyAsync(cancellationToken); - var presentationManifest = await rawRequestBody.ToPresentation(); - - if (presentationManifest == null) + var presentationManifest = await rawRequestBody.TryDeserializePresentation(); + + if (presentationManifest.Error) { return this.PresentationProblem("Could not deserialize manifest", null, (int) HttpStatusCode.BadRequest, "Deserialization Error"); } - var validation = await validator.ValidateAsync(presentationManifest, cancellationToken); + var validation = await validator.ValidateAsync(presentationManifest.ConvertedIIIF!, cancellationToken); if (!validation.IsValid) { return this.ValidationFailed(validation); } - return await HandleUpsert(requestFactory(presentationManifest, rawRequestBody), instance, errorTitle, + return await HandleUpsert(requestFactory(presentationManifest.ConvertedIIIF!, rawRequestBody), instance, errorTitle, cancellationToken); } -} \ No newline at end of file +} diff --git a/src/IIIFPresentation/API/Features/Storage/CollectionController.cs b/src/IIIFPresentation/API/Features/Storage/CollectionController.cs index d050b82a..f93088d5 100644 --- a/src/IIIFPresentation/API/Features/Storage/CollectionController.cs +++ b/src/IIIFPresentation/API/Features/Storage/CollectionController.cs @@ -97,7 +97,8 @@ private async Task> Deserial var rawRequestBody = await Request.GetRawRequestBodyAsync(); - var deserializedCollection = await rawRequestBody.TryDeserializePresentationCollection(); + var deserializedCollection = + await rawRequestBody.TryDeserializePresentation(); if (deserializedCollection.Error) { return DeserializeValidationResult.Failure(PresentationUnableToSerialize()); diff --git a/src/IIIFPresentation/API/Features/Storage/Helpers/RequestBodyX.cs b/src/IIIFPresentation/API/Features/Storage/Helpers/RequestBodyX.cs index d62021da..9fa98b2f 100644 --- a/src/IIIFPresentation/API/Features/Storage/Helpers/RequestBodyX.cs +++ b/src/IIIFPresentation/API/Features/Storage/Helpers/RequestBodyX.cs @@ -2,6 +2,7 @@ using Core.IIIF; using IIIF; using IIIF.Serialisation; +using Models.API; using Models.API.Collection; namespace API.Features.Storage.Helpers; @@ -35,19 +36,20 @@ public static TryConvertIIIFResult ConvertCollectionToIIIF(this string req /// /// The raw request body to convert /// A result containing the deserialized collection, or a failure - public static async Task> TryDeserializePresentationCollection(this string requestBody) + public static async Task> TryDeserializePresentation(this string requestBody) + where T : JsonLdBase, new() { try { - var collection = await requestBody.ToPresentation(); + var collection = await requestBody.ToPresentation(); return collection == null - ? TryConvertIIIFResult.Failure() - : TryConvertIIIFResult.Success(collection); + ? TryConvertIIIFResult.Failure() + : TryConvertIIIFResult.Success(collection); } catch (Exception) { - return TryConvertIIIFResult.Failure(); + return TryConvertIIIFResult.Failure(); } } -} \ No newline at end of file +} From b8b493cd51420c348bd3ec0432a34f6f775bec13 Mon Sep 17 00:00:00 2001 From: "jack.lewis" Date: Wed, 15 Jan 2025 11:33:57 +0000 Subject: [PATCH 2/2] Adding test --- .../Integration/ModifyManifestUpdateTests.cs | 39 +++++++++++++++++++ .../Features/Manifest/ManifestController.cs | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/IIIFPresentation/API.Tests/Integration/ModifyManifestUpdateTests.cs b/src/IIIFPresentation/API.Tests/Integration/ModifyManifestUpdateTests.cs index b14b59e7..e076df8e 100644 --- a/src/IIIFPresentation/API.Tests/Integration/ModifyManifestUpdateTests.cs +++ b/src/IIIFPresentation/API.Tests/Integration/ModifyManifestUpdateTests.cs @@ -506,4 +506,43 @@ public async Task PutFlatId_Update_IgnoresPaintedResources_WhenItemsAndPaintedRe .Be("https://iiif.example/manifestFromItems.json"); presentationManifest.Items.Count.Should().Be(1); } + + [Fact] + public async Task? CreateManifest_ReturnsNiceError_WhenDeserializationError() + { + // Arrange + var slug = nameof(CreateManifest_ReturnsNiceError_WhenDeserializationError); + var manifest = $$""" + { + "slug": "{{slug}}", + "parent": "https://presentation-api.dlcs.digirati.io/52/collections/root", + "type": "Manifest", + "label": { + "en": [ + "Example Manifest" + ] + }, + "behavior": [ + "paged" + ], + "items": [ + { + "my_external_manifest": "https://digirati.io/resources/cheese.json" + } + ] + } + """; + + var requestMessage = + HttpRequestMessageBuilder.GetPrivateRequest(HttpMethod.Put, $"{Customer}/manifests/brokenManifest", + manifest); + + // Act + var response = await httpClient.AsCustomer().SendAsync(requestMessage); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + var errorResponse = await response.ReadAsPresentationResponseAsync(); + errorResponse!.Detail.Should().Be("Could not deserialize manifest"); + } } diff --git a/src/IIIFPresentation/API/Features/Manifest/ManifestController.cs b/src/IIIFPresentation/API/Features/Manifest/ManifestController.cs index a13ffef2..bf16da34 100644 --- a/src/IIIFPresentation/API/Features/Manifest/ManifestController.cs +++ b/src/IIIFPresentation/API/Features/Manifest/ManifestController.cs @@ -103,7 +103,7 @@ private async Task ManifestUpsert( var rawRequestBody = await Request.GetRawRequestBodyAsync(cancellationToken); var presentationManifest = await rawRequestBody.TryDeserializePresentation(); - + if (presentationManifest.Error) { return this.PresentationProblem("Could not deserialize manifest", null, (int) HttpStatusCode.BadRequest,