Skip to content

Commit

Permalink
Merge pull request #871 from dlcs/feature/empty_members
Browse files Browse the repository at this point in the history
Allow batch with empty 'members' in legacy mode
  • Loading branch information
donaldgray authored Jun 26, 2024
2 parents 7856018 + ee3f39d commit 5aa8eeb
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 3 deletions.
44 changes: 44 additions & 0 deletions src/protagonist/API.Tests/Integration/CustomerQueueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,30 @@ public async Task Post_CreateBatch_201_IfLegacyModeEnabled()
response.StatusCode.Should().Be(HttpStatusCode.Created);
}

[Fact]
public async Task Post_CreateBatch_201_IfLegacyModeEnabled_MembersEmpty()
{
// Arrange
var hydraImageBody = @"{
""@context"": ""http://www.w3.org/ns/hydra/context.jsonld"",
""@type"": ""Collection"",
""member"": []
}";

var content = new StringContent(hydraImageBody, Encoding.UTF8, "application/json");
var path = $"/customers/{LegacyModeHelpers.LegacyCustomer}/queue";

// Act
var response = await httpClient.AsCustomer(LegacyModeHelpers.LegacyCustomer).PostAsync(path, content);

// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);

var model = await response.ReadAsHydraResponseAsync<DLCS.HydraModel.CustomerQueue>();
var dbBatch = dbContext.Batches.Single(a => a.Id == model.Id.GetLastPathElementAsInt());
dbBatch.Count.Should().Be(0);
}

[Fact]
public async Task Post_CreateBatch_201_IfLegacyModeEnabledWithAtIdFieldSet()
{
Expand Down Expand Up @@ -698,6 +722,26 @@ public async Task Post_CreateBatch_400_WithInvalidIdsSet()
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}

[Fact]
public async Task Post_CreateBatch_400_MembersEmpty()
{
// Arrange
var hydraImageBody = @"{
""@context"": ""http://www.w3.org/ns/hydra/context.jsonld"",
""@type"": ""Collection"",
""member"": []
}";

var content = new StringContent(hydraImageBody, Encoding.UTF8, "application/json");
const string path = "/customers/99/queue";

// Act
var response = await httpClient.AsCustomer(99).PostAsync(path, content);

// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}

[Fact]
public async Task Post_CreateBatch_400_IfSpaceNotFound()
{
Expand Down
8 changes: 7 additions & 1 deletion src/protagonist/API/Converters/LegacyModeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using DLCS.HydraModel;
using DLCS.Model.Assets;
using Hydra;
using Microsoft.Extensions.Logging;
using AssetFamily = DLCS.HydraModel.AssetFamily;

namespace API.Converters;
Expand All @@ -14,13 +15,16 @@ namespace API.Converters;
public static class LegacyModeConverter
{
private const string DefaultMediaType = "image/unknown";

internal static void LogLegacyUsage(this ILogger logger, string message, params object?[] args)
=> logger.LogWarning("LEGACY USE:" + message, args);

/// <summary>
/// Converts from legacy format to new format
/// </summary>
/// <param name="image">The image to convert should be emulated and translated into delivery channels</param>
/// <returns>A converted image</returns>
public static T VerifyAndConvertToModernFormat<T>(T image)
public static T VerifyAndConvertToModernFormat<T>(T image, ILogger? logger = null)
where T : Image
{
if (image.Origin.IsNullOrEmpty())
Expand All @@ -30,6 +34,7 @@ public static T VerifyAndConvertToModernFormat<T>(T image)

if (image.MediaType.IsNullOrEmpty())
{
logger?.LogLegacyUsage("Null or empty media type");
var contentType = image.Origin?.Split('.').Last() ?? string.Empty;

image.MediaType = MIMEHelper.GetContentTypeForExtension(contentType) ?? DefaultMediaType;
Expand All @@ -43,6 +48,7 @@ public static T VerifyAndConvertToModernFormat<T>(T image)

if (image.MaxUnauthorised is null or 0 && image.Roles.IsNullOrEmpty())
{
logger?.LogLegacyUsage("MaxUnauthorised");
image.MaxUnauthorised = -1;
}

Expand Down
18 changes: 16 additions & 2 deletions src/protagonist/API/Features/Queues/CustomerQueueController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Net;
using API.Converters;
using API.Exceptions;
using API.Features.Image;
Expand All @@ -8,6 +7,7 @@
using API.Features.Queues.Validation;
using API.Infrastructure;
using API.Settings;
using DLCS.Core.Collections;
using DLCS.Core.Strings;
using DLCS.HydraModel;
using DLCS.Model.Assets;
Expand All @@ -16,6 +16,7 @@
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Batch = DLCS.HydraModel.Batch;

Expand All @@ -28,8 +29,12 @@ namespace API.Features.Queues;
[ApiController]
public class CustomerQueueController : HydraController
{
public CustomerQueueController(IOptions<ApiSettings> settings, IMediator mediator) : base(settings.Value, mediator)
private readonly ILogger<CustomerQueueController> logger;

public CustomerQueueController(IOptions<ApiSettings> settings, IMediator mediator,
ILogger<CustomerQueueController> logger) : base(settings.Value, mediator)
{
this.logger = logger;
}

/// <summary>
Expand Down Expand Up @@ -331,6 +336,15 @@ private async Task<IActionResult> CreateBatchInternal(int customerId, HydraColle
try
{
UpdateMembers(customerId, images.Members);

if (images.Members.IsEmpty() && Settings.LegacyModeEnabledForCustomer(customerId))
{
logger.LogLegacyUsage("Empty batch received for customer {CustomerId}", customerId);
return await HandleUpsert(new CreateEmptyBatch(customerId),
batch => batch.ToHydra(GetUrlRoots().BaseUrl),
errorTitle: "Create batch failed",
cancellationToken: cancellationToken);
}
}
catch (APIException apiEx)
{
Expand Down
35 changes: 35 additions & 0 deletions src/protagonist/API/Features/Queues/Requests/CreateEmptyBatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using API.Infrastructure.Requests;
using DLCS.Core;
using DLCS.Model.Assets;
using MediatR;

namespace API.Features.Queues.Requests;

/// <summary>
/// Handler that creates an empty batch of 0 images
/// </summary>
public class CreateEmptyBatch : IRequest<ModifyEntityResult<Batch>>
{
public int CustomerId { get; }

public CreateEmptyBatch(int customerId)
{
CustomerId = customerId;
}
}

public class CreateEmptyBatchHandler : IRequestHandler<CreateEmptyBatch, ModifyEntityResult<Batch>>
{
private readonly IBatchRepository batchRepository;

public CreateEmptyBatchHandler(IBatchRepository batchRepository)
{
this.batchRepository = batchRepository;
}

public async Task<ModifyEntityResult<Batch>> Handle(CreateEmptyBatch request, CancellationToken cancellationToken)
{
var batch = await batchRepository.CreateBatch(request.CustomerId, Array.Empty<Asset>(), cancellationToken);
return ModifyEntityResult<Batch>.Success(batch, WriteResult.Created);
}
}
24 changes: 24 additions & 0 deletions src/protagonist/DLCS.Core.Tests/Collections/CollectionXTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,30 @@ public void IsNullOrEmpty_List_False_IfHasValues()

coll.IsNullOrEmpty().Should().BeFalse();
}

[Fact]
public void IsEmpty_List_False_IfNull()
{
List<int> coll = null;

coll.IsEmpty().Should().BeFalse();
}

[Fact]
public void IsEmpty_List_True_IfEmpty()
{
var coll = new List<int>();

coll.IsEmpty().Should().BeTrue();
}

[Fact]
public void IsEmpty_List_False_IfHasValues()
{
var coll = new List<int> {2};

coll.IsEmpty().Should().BeFalse();
}

[Fact]
public void AsList_ReturnsExpected()
Expand Down
7 changes: 7 additions & 0 deletions src/protagonist/DLCS.Core/Collections/CollectionX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ public static bool IsNullOrEmpty<T>([NotNullWhen(false)] this IEnumerable<T>? co
/// <returns>true if null or empty, else false</returns>
public static bool IsNullOrEmpty<T>([NotNullWhen(false)] this IList<T>? collection)
=> collection == null || collection.Count == 0;

/// <summary>
/// Check if IList is empty - this explicitly checks for Empty only, list could still be null
/// </summary>
/// <returns>true if empty, else false</returns>
public static bool IsEmpty<T>(this IList<T>? collection)
=> collection?.Count == 0;

/// <summary>
/// Check if list contains single specified item
Expand Down

0 comments on commit 5aa8eeb

Please sign in to comment.