Skip to content

Commit

Permalink
Merge pull request #734 from dlcs/hydra_new_deliverychannels
Browse files Browse the repository at this point in the history
Add Hydra model for delivery channels
  • Loading branch information
griffri committed Feb 14, 2024
2 parents d2f8cf4 + d98c70f commit 80d334e
Show file tree
Hide file tree
Showing 13 changed files with 84 additions and 53 deletions.
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.",
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", "PATCH", "DELETE");
}
}
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

0 comments on commit 80d334e

Please sign in to comment.