Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Hydra model for delivery channels #734

Merged
merged 4 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
4 changes: 2 additions & 2 deletions src/protagonist/API.Tests/Converters/AssetConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public void ToDlcsModel_All_Fields_Should_Convert()
MaxUnauthorised = 400,
MediaType = mediaType,
ThumbnailPolicy = thumbnailPolicy,
DeliveryChannels = deliveryChannel
WcDeliveryChannels = deliveryChannel
};

var asset = hydraImage.ToDlcsModel(1);
Expand Down Expand Up @@ -174,7 +174,7 @@ public void ToDlcsModel_ReordersDeliveryChannel()
{
Id = AssetApiId,
Space = 99,
DeliveryChannels = deliveryChannel
WcDeliveryChannels = deliveryChannel
};

// Act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void Width_Provided_NotFileOnly_OrAudio(string mediaType, string dc)
{
var model = new DLCS.HydraModel.Image
{
Width = 10, DeliveryChannels = dc.Split(","), MediaType = mediaType
Width = 10, WcDeliveryChannels = dc.Split(","), MediaType = mediaType
};
var result = sut.TestValidate(model);
result
Expand All @@ -70,7 +70,7 @@ public void Width_Allowed_IfFileOnly_AndVideoOrImage(string mediaType)
{
var model = new DLCS.HydraModel.Image
{
MediaType = mediaType, DeliveryChannels = new[] { "file" }, Width = 10
MediaType = mediaType, WcDeliveryChannels = new[] { "file" }, Width = 10
};
var result = sut.TestValidate(model);
result.ShouldNotHaveValidationErrorFor(a => a.Width);
Expand All @@ -94,7 +94,7 @@ public void Height_Provided_NotFileOnly_OrAudio(string mediaType, string dc)
{
var model = new DLCS.HydraModel.Image
{
Height = 10, DeliveryChannels = dc.Split(","), MediaType = mediaType
Height = 10, WcDeliveryChannels = dc.Split(","), MediaType = mediaType
};
var result = sut.TestValidate(model);
result
Expand All @@ -110,7 +110,7 @@ public void Height_Allowed_IfFileOnly_AndVideoOrImage(string mediaType)
{
var model = new DLCS.HydraModel.Image
{
MediaType = mediaType, DeliveryChannels = new[] { "file" }, Height = 10
MediaType = mediaType, WcDeliveryChannels = new[] { "file" }, Height = 10
};
var result = sut.TestValidate(model);
result.ShouldNotHaveValidationErrorFor(a => a.Height);
Expand All @@ -134,7 +134,7 @@ public void Duration_Provided_NotFileOnly_OrImage(string mediaType, string dc)
{
var model = new DLCS.HydraModel.Image
{
Duration = 10, DeliveryChannels = dc.Split(","), MediaType = mediaType
Duration = 10, WcDeliveryChannels = dc.Split(","), MediaType = mediaType
};
var result = sut.TestValidate(model);
result
Expand All @@ -150,7 +150,7 @@ public void Duration_Allowed_IfFileOnly_AndVideoOrAudio(string mediaType)
{
var model = new DLCS.HydraModel.Image
{
MediaType = mediaType, DeliveryChannels = new[] { "file" }, Duration = 10
MediaType = mediaType, WcDeliveryChannels = new[] { "file" }, Duration = 10
};
var result = sut.TestValidate(model);
result.ShouldNotHaveValidationErrorFor(a => a.Duration);
Expand Down Expand Up @@ -180,7 +180,7 @@ public void UseOriginalPolicy_NotImage(string dc)
{
var model = new DLCS.HydraModel.Image
{
DeliveryChannels = dc.Split(","),
WcDeliveryChannels = dc.Split(","),
MediaType = "image/jpeg",
ImageOptimisationPolicy = KnownImageOptimisationPolicy.UseOriginalId
};
Expand All @@ -197,7 +197,7 @@ public void UseOriginalPolicy_Image(string dc)
{
var model = new DLCS.HydraModel.Image
{
DeliveryChannels = dc.Split(","),
WcDeliveryChannels = dc.Split(","),
MediaType = "image/jpeg",
ImageOptimisationPolicy = KnownImageOptimisationPolicy.UseOriginalId
};
Expand All @@ -210,7 +210,7 @@ public void DeliveryChannel_CanBeEmpty()
{
var model = new DLCS.HydraModel.Image();
var result = sut.TestValidate(model);
result.ShouldNotHaveValidationErrorFor(a => a.DeliveryChannels);
result.ShouldNotHaveValidationErrorFor(a => a.WcDeliveryChannels);
}

[Theory]
Expand All @@ -220,17 +220,17 @@ public void DeliveryChannel_CanBeEmpty()
[InlineData("file,iiif-av,iiif-img")]
public void DeliveryChannel_CanContainKnownValues(string knownValues)
{
var model = new DLCS.HydraModel.Image { DeliveryChannels = knownValues.Split(',') };
var model = new DLCS.HydraModel.Image { WcDeliveryChannels = knownValues.Split(',') };
var result = sut.TestValidate(model);
result.ShouldNotHaveValidationErrorFor(a => a.DeliveryChannels);
result.ShouldNotHaveValidationErrorFor(a => a.WcDeliveryChannels);
}

[Fact]
public void DeliveryChannel_UnknownValue()
{
var model = new DLCS.HydraModel.Image { DeliveryChannels = new[] { "foo" } };
var model = new DLCS.HydraModel.Image { WcDeliveryChannels = new[] { "foo" } };
var result = sut.TestValidate(model);
result.ShouldHaveValidationErrorFor(a => a.DeliveryChannels);
result.ShouldHaveValidationErrorFor(a => a.WcDeliveryChannels);
}


Expand All @@ -239,9 +239,9 @@ public void DeliveryChannel_ValidationError_WhenDeliveryChannelsDisabled()
{
var apiSettings = new ApiSettings();
var imageValidator = new HydraImageValidator(Options.Create(apiSettings));
var model = new DLCS.HydraModel.Image { DeliveryChannels = new[] { "iiif-img" } };
var model = new DLCS.HydraModel.Image { WcDeliveryChannels = new[] { "iiif-img" } };
var result = imageValidator.TestValidate(model);
result.ShouldHaveValidationErrorFor(a => a.DeliveryChannels);
result.ShouldHaveValidationErrorFor(a => a.WcDeliveryChannels);
}
[Fact]
public void DeliveryChannel_NoValidationError_WhenDeliveryChannelsDisabled()
Expand All @@ -250,6 +250,6 @@ public void DeliveryChannel_NoValidationError_WhenDeliveryChannelsDisabled()
var imageValidator = new HydraImageValidator(Options.Create(apiSettings));
var model = new DLCS.HydraModel.Image();
var result = imageValidator.TestValidate(model);
result.ShouldNotHaveValidationErrorFor(a => a.DeliveryChannels);
result.ShouldNotHaveValidationErrorFor(a => a.WcDeliveryChannels);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ public void Member_DeliveryChannels_Provided()
{
var model = new HydraCollection<Image> { Members = new[]
{
new Image { DeliveryChannels = new []{"iiif-img","thumbs"}}
new Image { WcDeliveryChannels = new []{"iiif-img","thumbs"}}
} };
var result = sut.TestValidate(model);
result.ShouldHaveValidationErrorFor("Members[0].DeliveryChannels");
result.ShouldHaveValidationErrorFor("Members[0].WcDeliveryChannels");
}

[Fact]
Expand Down
4 changes: 2 additions & 2 deletions src/protagonist/API.Tests/Integration/CustomerQueueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -997,14 +997,14 @@ public async Task Post_CreatePriorityBatch_UpdatesQueueAndCounts()
""id"": ""one"",
""origin"": ""https://example.org/foo.jpg"",
""space"": 2,
""deliveryChannels"": [""iiif-img""],
""wcDeliveryChannels"": [""iiif-img""],
""family"": ""I"",
""mediaType"": ""image/jpeg""
},
{
""id"": ""two"",
""origin"": ""https://example.org/foo.png"",
""deliveryChannels"": [""iiif-img""],
""wcDeliveryChannels"": [""iiif-img""],
""space"": 2,
""mediaType"": ""image/png""
},
Expand Down
10 changes: 4 additions & 6 deletions src/protagonist/API.Tests/Integration/ModifyAssetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -542,18 +542,16 @@ public async Task Put_New_Asset_Requires_Origin()
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}

[Theory]
[InlineData("deliveryChannels")]
[InlineData("wcDeliveryChannels")]
public async Task Put_New_Asset_Supports_DeliveryChannels_Aliases(string deliveryChannelAlias)
[Fact]
public async Task Put_New_Asset_Supports_WcDeliveryChannels()
{
var assetId = new AssetId(99, 1, $"{nameof(Put_New_Asset_Supports_DeliveryChannels_Aliases)}-{deliveryChannelAlias}");
var assetId = new AssetId(99, 1, nameof(Put_New_Asset_Supports_WcDeliveryChannels));
var hydraImageBody = $@"{{
""@type"": ""Image"",
""origin"": ""https://example.org/{assetId.Asset}.tiff"",
""family"": ""I"",
""mediaType"": ""image/tiff"",
""{deliveryChannelAlias}"": [""file""]
""wcDeliveryChannels"": [""file""]
}}";

A.CallTo(() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,15 @@ public class ModifyAssetWithoutDeliveryChannelsTests : IClassFixture<Protagonist
});
}

[Theory]
[InlineData("deliveryChannels")]
[InlineData("wcDeliveryChannels")]
public async Task Patch_Asset_Fails_When_Delivery_Channels_Are_Disabled(string deliveryChannelAlias)
[Fact]
public async Task Patch_Asset_Fails_When_Delivery_Channels_Are_Disabled()
{
// Arrange
var assetId = new AssetId(99, 1, $"{nameof(Patch_Asset_Fails_When_Delivery_Channels_Are_Disabled)}");
var hydraImageBody = $@"{{
""@type"": ""Image"",
""string1"": ""I am edited"",
""{deliveryChannelAlias}"": [
""wcDeliveryChannels"": [
""iiif-img""
]
}}";
Expand Down
6 changes: 3 additions & 3 deletions src/protagonist/API/Converters/AssetConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static Image ToHydra(this Asset dbAsset, UrlRoots urlRoots)
MediaType = dbAsset.MediaType,
Family = (AssetFamily)dbAsset.Family,
Roles = dbAsset.RolesList.ToArray(),
DeliveryChannels = dbAsset.DeliveryChannels
WcDeliveryChannels = dbAsset.DeliveryChannels
};

if (dbAsset.Batch > 0)
Expand Down Expand Up @@ -270,9 +270,9 @@ public static Asset ToDlcsModel(this Image hydraImage, int customerId, int? spac
asset.MediaType = hydraImage.MediaType;
}

if (hydraImage.DeliveryChannels != null)
if (hydraImage.WcDeliveryChannels != null)
{
asset.DeliveryChannels = hydraImage.DeliveryChannels.OrderBy(dc => dc).Select(dc => dc.ToLower()).ToArray();
asset.DeliveryChannels = hydraImage.WcDeliveryChannels.OrderBy(dc => dc).Select(dc => dc.ToLower()).ToArray();
}

var thumbnailPolicy = hydraImage.ThumbnailPolicy.GetLastPathElement("thumbnailPolicies/");
Expand Down
2 changes: 1 addition & 1 deletion src/protagonist/API/Converters/LegacyModeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static T VerifyAndConvertToModernFormat<T>(T image)

image.MediaType = MIMEHelper.GetContentTypeForExtension(contentType) ?? DefaultMediaType;

if (image.Origin is not null && image.Family is null && image.DeliveryChannels.IsNullOrEmpty())
if (image.Origin is not null && image.Family is null && image.WcDeliveryChannels.IsNullOrEmpty())
{
image.Family = AssetFamily.Image;
}
Expand Down
2 changes: 1 addition & 1 deletion src/protagonist/API/Features/Image/ImageController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public async Task<IActionResult> GetImage(int customerId, int spaceId, string im
[FromBody] DLCS.HydraModel.Image hydraAsset,
CancellationToken cancellationToken)
{
if (!apiSettings.DeliveryChannelsEnabled && !hydraAsset.DeliveryChannels.IsNullOrEmpty())
if (!apiSettings.DeliveryChannelsEnabled && !hydraAsset.WcDeliveryChannels.IsNullOrEmpty())
{
var assetId = new AssetId(customerId, spaceId, imageId);
return this.HydraProblem("Delivery channels are disabled", assetId.ToString(), 400, "Bad Request");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public HydraImageValidator(IOptions<ApiSettings> apiSettings)
// Required fields
RuleFor(a => a.MediaType).NotEmpty().WithMessage("Media type must be specified");

When(a => !a.DeliveryChannels.IsNullOrEmpty(), DeliveryChannelDependantValidation)
When(a => !a.WcDeliveryChannels.IsNullOrEmpty(), DeliveryChannelDependantValidation)
.Otherwise(() =>
{
RuleFor(a => a.Width).Empty().WithMessage("Should not include width");
Expand All @@ -32,11 +32,11 @@ public HydraImageValidator(IOptions<ApiSettings> apiSettings)
RuleFor(a => a.Created).Empty().WithMessage("Should not include created");

// Other validation
RuleFor(a => a.DeliveryChannels).Must(d => d.IsNullOrEmpty())
RuleFor(a => a.WcDeliveryChannels).Must(d => d.IsNullOrEmpty())
.When(_ => !apiSettings.Value.DeliveryChannelsEnabled)
.WithMessage("Delivery channels are disabled");

RuleForEach(a => a.DeliveryChannels)
RuleForEach(a => a.WcDeliveryChannels)
.Must(dc => AssetDeliveryChannels.All.Contains(dc))
.WithMessage($"DeliveryChannel must be one of {AssetDeliveryChannels.AllString}");
}
Expand All @@ -46,31 +46,31 @@ private void DeliveryChannelDependantValidation()
{
RuleFor(a => a.ImageOptimisationPolicy)
.Must(iop => !KnownImageOptimisationPolicy.IsNoOpIdentifier(iop))
.When(a => !a.DeliveryChannels.ContainsOnly(AssetDeliveryChannels.File))
.When(a => !a.WcDeliveryChannels.ContainsOnly(AssetDeliveryChannels.File))
.WithMessage(
$"ImageOptimisationPolicy {KnownImageOptimisationPolicy.NoneId} only valid for 'file' delivery channel");

RuleFor(a => a.Width)
.Empty()
.WithMessage("Should not include width")
.Unless(a =>
a.DeliveryChannels.ContainsOnly(AssetDeliveryChannels.File) && !MIMEHelper.IsAudio(a.MediaType));
a.WcDeliveryChannels.ContainsOnly(AssetDeliveryChannels.File) && !MIMEHelper.IsAudio(a.MediaType));

RuleFor(a => a.Height)
.Empty()
.WithMessage("Should not include height")
.Unless(a =>
a.DeliveryChannels.ContainsOnly(AssetDeliveryChannels.File) && !MIMEHelper.IsAudio(a.MediaType));
a.WcDeliveryChannels.ContainsOnly(AssetDeliveryChannels.File) && !MIMEHelper.IsAudio(a.MediaType));

RuleFor(a => a.Duration)
.Empty()
.WithMessage("Should not include duration")
.Unless(a =>
a.DeliveryChannels.ContainsOnly(AssetDeliveryChannels.File) && !MIMEHelper.IsImage(a.MediaType));
a.WcDeliveryChannels.ContainsOnly(AssetDeliveryChannels.File) && !MIMEHelper.IsImage(a.MediaType));

RuleFor(a => a.ImageOptimisationPolicy)
.Must(iop => !KnownImageOptimisationPolicy.IsUseOriginalIdentifier(iop))
.When(a => !a.DeliveryChannels!.Contains(AssetDeliveryChannels.Image))
.When(a => !a.WcDeliveryChannels!.Contains(AssetDeliveryChannels.Image))
.WithMessage(
$"ImageOptimisationPolicy '{KnownImageOptimisationPolicy.UseOriginalId}' only valid for image delivery-channel");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public ImageBatchPatchValidator(IOptions<ApiSettings> apiSettings)
members.RuleFor(a => a.Origin).Empty().WithMessage("Origin cannot be set in a bulk patching operation");
members.RuleFor(a => a.ImageOptimisationPolicy).Empty().WithMessage("Image optimisation policies cannot be set in a bulk patching operation");
members.RuleFor(a => a.MaxUnauthorised).Empty().WithMessage("MaxUnauthorised cannot be set in a bulk patching operation");
members.RuleFor(a => a.DeliveryChannels).Empty().WithMessage("Delivery channels cannot be set in a bulk patching operation");
members.RuleFor(a => a.WcDeliveryChannels).Empty().WithMessage("Delivery channels cannot be set in a bulk patching operation");
members.RuleFor(a => a.ThumbnailPolicy).Empty().WithMessage("Thumbnail policy cannot be set in a bulk patching operation");
});
}
Expand Down
38 changes: 38 additions & 0 deletions src/protagonist/DLCS.HydraModel/DeliveryChannel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Hydra;
using Hydra.Model;
using Newtonsoft.Json;

namespace DLCS.HydraModel;

[HydraClass(typeof(DeliveryChannelClass),
Description = "A delivery channel represents a way an asset on the DLCS can be served.",
UriTemplate = "")]
public class DeliveryChannel : DlcsResource
{
[RdfProperty(Description = "The name of the DLCS delivery channel this is based on.",
griffri marked this conversation as resolved.
Show resolved Hide resolved
Range = Names.XmlSchema.String, ReadOnly = false, WriteOnly = false)]
[JsonProperty(Order = 11, PropertyName = "channel")]
public string? Channel { get; set; }

[HydraLink(Description = "The policy assigned to this delivery channel.",
Range = "vocab:deliveryChannelPolicy", ReadOnly = false, WriteOnly = false)]
[JsonProperty(Order = 12, PropertyName = "policy")]
public string? Policy { get; set; }
}

public class DeliveryChannelClass: Class
{
string operationId = "_:deliveryChannel_";

public DeliveryChannelClass()
{
BootstrapViaReflection(typeof(DeliveryChannel));
}

public override void DefineOperations()
{
SupportedOperations = CommonOperations.GetStandardResourceOperations(
operationId, "Delivery Channel", Id,
"GET", "POST", "PUT");
griffri marked this conversation as resolved.
Show resolved Hide resolved
}
}
9 changes: 3 additions & 6 deletions src/protagonist/DLCS.HydraModel/Image.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,18 +195,15 @@ public Image(string baseUrl, int customerId, int space, string modelId)
[JsonProperty(Order = 130, PropertyName = "textType")]
public string? TextType { get; set; } // e.g., METS-ALTO, hOCR, TEI, text/plain etc

[JsonIgnore]
public string[]? DeliveryChannels { get; set; }

[RdfProperty(Description = "Delivery channel specifying how the asset will be available.",
Range = Names.XmlSchema.String, ReadOnly = false, WriteOnly = true)]
Range = Names.XmlSchema.String, ReadOnly = false, WriteOnly = false)]
[JsonProperty(Order = 140, PropertyName = "deliveryChannels")]
public string[]? OldDeliveryChannels { set => DeliveryChannels = value; get => DeliveryChannels; }
public DeliveryChannel[]? DeliveryChannels { get; set; }

[RdfProperty(Description = "Delivery channel specifying how the asset will be available.",
Range = Names.XmlSchema.String, ReadOnly = false, WriteOnly = false)]
[JsonProperty(Order = 141, PropertyName = "wcDeliveryChannels")]
public string[]? WcDeliveryChannels { set => DeliveryChannels = value; get => DeliveryChannels; }
public string[]? WcDeliveryChannels { get; set; }

[RdfProperty(Description = "The role or roles that a user must possess to view this image above maxUnauthorised. " +
"These are URIs of roles e.g., https://api.dlcs.io/customers/1/roles/requiresRegistration",
Expand Down
Loading