Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e8a554b
Manifest path test
p-kaczynski Nov 5, 2025
cdedc7e
Implement handling `id` in manifest body
p-kaczynski Nov 11, 2025
b7f2d46
Add differentiation of hierarchical posts manifest/collection
p-kaczynski Nov 13, 2025
153355f
Add tests for valid collection creation paths
p-kaczynski Nov 17, 2025
d791b3b
Implement/fix POST/PUT hierarchical collection endpoints per tests
p-kaczynski Nov 17, 2025
0a9c377
Make UpsertCollection return Created/Updated result correctly
p-kaczynski Nov 18, 2025
2e2d8ab
Update Collection PUT/POST tests
p-kaczynski Nov 18, 2025
a0126a2
Create CollectionWriteRequest.cs
p-kaczynski Nov 19, 2025
718558a
Add class that produced "pure" IIIF from our Presentation* classes
p-kaczynski Nov 26, 2025
80f0470
Add test method `HttpRequestMessageBuilder.GetPlainRequest` that can …
p-kaczynski Nov 26, 2025
539ffa3
Add `Forbidden` result/error
p-kaczynski Nov 26, 2025
bd6f7c8
Update `ModifyCollectionTests` as per docs
p-kaczynski Nov 26, 2025
8d43be6
Handle `Forbidden` result in Controller base
p-kaczynski Nov 26, 2025
b97b90f
Add explicit `ExtraHeaderRequired` to modify collection type enumeration
p-kaczynski Nov 26, 2025
3463487
Prevent invalid collection hierarchy retrieval if provided slug ends …
p-kaczynski Nov 26, 2025
d65fbfb
Unify collection handling to support all required scenarios
p-kaczynski Nov 26, 2025
501ae47
Include ETags in collection tests per new functionality
p-kaczynski Nov 27, 2025
d8edee2
Add necessary root validation
p-kaczynski Nov 27, 2025
91a230a
TEMP: Manifest handling
p-kaczynski Nov 27, 2025
4fb40c9
Revert previous id handling from MWS
p-kaczynski Dec 1, 2025
ed53e4a
Implement a unified manifest dispatch request
p-kaczynski Dec 1, 2025
f8f231d
Update to use new DispatchManifestRequest
p-kaczynski Dec 1, 2025
a4622ce
Fix validation in updated manifest handling
p-kaczynski Dec 2, 2025
495e86a
Add code that checks hierarchical parent from path with flat parent f…
p-kaczynski Dec 2, 2025
b1a3272
Fix invalid test cases
p-kaczynski Dec 2, 2025
5cb2849
Clean-up code, update comments and ensure all cases are handled
p-kaczynski Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using API.Converters;

namespace API.Tests.Converters;

public class FastJsonPropertyReadTests
{
[Fact]
public void FindAtLevel_FindsValue_DefaultTopLevel()
{
FastJsonPropertyRead.FindAtLevel(Json, "searchedProperty").Should()
.Be("fnord", "it's the value of 'searchedProperty' in sample JSON");
}

[Fact]
public void FindAtLevel_FindsValue_Deeper()
{
FastJsonPropertyRead.FindAtLevel(Json, "priority", 3).Should()
.Be("high", "it's the value of first instance of 'priority' property in sample JSON");
}

[Fact]
public void FindAtLevel_ReturnsNull_IfNotFound()
{
FastJsonPropertyRead.FindAtLevel(Json, "i don't exist in the json").Should()
.BeNull("no such property in the JSON");
}

// just some arbitrary JSON - the method under test is not IIIF specific
private const string Json =

#region <sample json>

"""
{
"id": 42,
"name": "Sample Root",
"enabled": true,
"tags": ["alpha", "beta", "gamma"],
"metadata": {
"created": "2025-02-15T12:34:56Z",
"updated": "2025-03-01T08:12:45Z",
"attributes": {
"priority": "high",
"flags": {
"archived": false,
"requiresReview": true,
"internal": {
"level": 3,
"notes": ["check format", "verify fields"]
}
}
}
},
"items": [
{
"id": "a1",
"quantity": 10,
"details": {
"manufacturer": "Acme Corp",
"dimensions": {
"width": 10.5,
"height": 20.0,
"depth": 5.75
}
}
},
{
"id": "b2",
"quantity": 4,
"details": {
"manufacturer": "Globex",
"dimensions": {
"width": 5.0,
"height": 8.0,
"depth": 1.25,
"history": [
{
"revision": 1,
"notes": { "editor": "system", "timestamp": "2025-01-01T00:00:00Z" }
},
{
"revision": 2,
"notes": { "editor": "qa", "timestamp": "2025-02-01T00:00:00Z" }
}
]
}
}
}
],
"config": {
"mode": "full",
"retry": 3,
"options": {
"validate": true,
"paths": [
{ "name": "input", "value": "/var/data/in" },
{ "name": "output", "value": "/var/data/out" }
]
}
},
"searchedProperty": "fnord"
}
""";

#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using API.Converters;
using IIIF.Presentation.V3;
using IIIF.Presentation.V3.Content;
using IIIF.Presentation.V3.Strings;
using Models.API.Collection;
using Models.API.Manifest;

namespace API.Tests.Converters;

public class PresentationIIIFCleanerTests
{
[Fact]
public void TestCleanManifest()
{
var manifest = new PresentationManifest
{
// From IIIF
Id = "this/is/some/Id",
Label = new LanguageMap("en", "some label"),
Thumbnail =
[
new Image
{
Id = "https://localhost/thumbs/12/23/blabla/full/143,200/0/default.jpg"
}
],
Rights = "https://creativecommons.org/licenses/by/4.0/",
Items =
[
new Canvas
{
Id = "some id"
}
]
// outside IIIF
,
Slug = "some slug",
Parent = "some parent",
PublicId = "some public id"
};

var clean = PresentationIIIFCleaner.OnlyIIIFProperties(manifest);

clean.Slug.Should().BeNull("not in IIIF");
clean.Parent.Should().BeNull("not in IIIF");
clean.PublicId.Should().BeNull("not in IIIF");

clean.Rights.Should().Be(manifest.Rights, "in the IIIF");
clean.Id.Should().Be(manifest.Id, "in the IIIF");
clean.Thumbnail.Should().BeEquivalentTo(manifest.Thumbnail, "in the IIIF");
clean.Items.Should().BeEquivalentTo(manifest.Items, "in the IIIF");
}

[Fact]
public void TestCleanCollection()
{
var collection = new PresentationCollection
{
// From IIIF
Id = "this/is/some/Id",
Label = new LanguageMap("en", "some label"),
Thumbnail =
[
new Image
{
Id = "https://localhost/thumbs/12/23/blabla/full/143,200/0/default.jpg"
}
],
Rights = "https://creativecommons.org/licenses/by/4.0/"
// outside IIIF
,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting?

Slug = "some slug",
Parent = "some parent",
PublicId = "some public id"
};

var clean = PresentationIIIFCleaner.OnlyIIIFProperties(collection);

clean.Slug.Should().BeNull("not in IIIF");
clean.Parent.Should().BeNull("not in IIIF");
clean.PublicId.Should().BeNull("not in IIIF");

clean.Rights.Should().Be(collection.Rights, "in the IIIF");
clean.Id.Should().Be(collection.Id, "in the IIIF");
clean.Thumbnail.Should().BeEquivalentTo(collection.Thumbnail, "in the IIIF");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ public class StorageCollection : ICollectionFixture<StorageFixture>
{
public const string CollectionName = "Storage Collection";
}
}

[CollectionDefinition(CollectionName, DisableParallelization = true)]
public class RestCollection : ICollectionFixture<StorageFixture>
{
public const string CollectionName = "Rest Collection";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ public static HttpRequestMessage GetPrivateRequest(HttpMethod method, string pat

return requestMessage;
}

public static HttpRequestMessage GetPlainRequest(HttpMethod method, string path, string content,
Guid? etag = null)
{
var requestMessage = new HttpRequestMessage(method, path).WithJsonContent(content);
if (etag is not null)
requestMessage.Headers.IfMatch.Add(new EntityTagHeaderValue($"\"{etag:N}\""));

return requestMessage;
}

public static HttpRequestMessage GetPrivateRequest(HttpMethod method, string path, Guid? etag = null)
{
Expand Down
Loading
Loading