Skip to content

Commit

Permalink
Merge pull request #836 from dlcs/feature/escapeNamedQuery
Browse files Browse the repository at this point in the history
Allow escaped forward slashes to be used in named query requests
  • Loading branch information
JackLewis-digirati committed May 1, 2024
2 parents 630d508 + a5d625b commit 9e1d84a
Show file tree
Hide file tree
Showing 8 changed files with 371 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public abstract class BaseNamedQueryParser<T> : INamedQueryParser
protected const string String2 = "s2";
protected const string String3 = "s3";
protected const string AssetOrdering = "assetOrder";
protected const string PathReplacement = "%2F";

public BaseNamedQueryParser(ILogger logger)
{
Expand Down Expand Up @@ -174,7 +175,7 @@ protected string GetQueryArgumentFromTemplateElement(List<string> args, string e
{
if (args.Count >= argNumber)
{
return args[argNumber - 1];
return args[argNumber - 1].Replace(PathReplacement, "/", StringComparison.OrdinalIgnoreCase);
}

throw new ArgumentOutOfRangeException(element,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Threading;
using DLCS.Model.Assets;
using DLCS.Model.Assets.NamedQueries;
using DLCS.Repository.NamedQueries.Models;
using Orchestrator.Infrastructure.NamedQueries.Persistence;

namespace Orchestrator.Tests.Integration.Infrastructure;

/// <summary>
/// Fake projection creator that handles configured callbacks for when ParsedNamedQuery is persisted.
/// Also optional callback for when ControlFile is created during persistence.
/// </summary>
public class FakePdfCreator : IProjectionCreator<PdfParsedNamedQuery>
{
private static readonly Dictionary<string, Func<ParsedNamedQuery, List<Asset>, bool>> Callbacks = new();

private static readonly Dictionary<string, Func<ControlFile, ControlFile>> ControlFileCallbacks = new();

public void AddCallbackFor(string pdfKey, Func<ParsedNamedQuery, List<Asset>, bool> callback)
=> Callbacks.Add(pdfKey, callback);

public void AddCallbackFor(string pdfKey, Func<ControlFile, ControlFile> callback)
=> ControlFileCallbacks.Add(pdfKey, callback);

public Task<(bool success, ControlFile controlFile)> PersistProjection(PdfParsedNamedQuery parsedNamedQuery,
List<Asset> images, CancellationToken cancellationToken = default)
{
if (Callbacks.TryGetValue(parsedNamedQuery.StorageKey, out var cb))
{
var controlFileCallback = ControlFileCallbacks.TryGetValue(parsedNamedQuery.StorageKey, out var cfcb)
? cfcb
: file => file;

return Task.FromResult((cb(parsedNamedQuery, images), controlFileCallback(new ControlFile())));
}

throw new Exception($"Request with key {parsedNamedQuery.StorageKey} not setup");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Threading;
using DLCS.Model.Assets;
using DLCS.Model.Assets.NamedQueries;
using DLCS.Repository.NamedQueries.Models;
using Orchestrator.Infrastructure.NamedQueries.Persistence;

namespace Orchestrator.Tests.Integration.Infrastructure;

public class FakeZipCreator : IProjectionCreator<ZipParsedNamedQuery>
{
private static readonly Dictionary<string, Func<ParsedNamedQuery, List<Asset>, bool>> callbacks = new();

/// <summary>
/// Add a callback for when zip is to be created and persisted to S3, allows control of success/failure for
/// testing
/// </summary>
public void AddCallbackFor(string s3Key, Func<ParsedNamedQuery, List<Asset>, bool> callback)
=> callbacks.Add(s3Key, callback);

public Task<(bool success, ControlFile controlFile)> PersistProjection(ZipParsedNamedQuery parsedNamedQuery, List<Asset> images,
CancellationToken cancellationToken = default)
{
if (callbacks.TryGetValue(parsedNamedQuery.StorageKey, out var cb))
{
return Task.FromResult((cb(parsedNamedQuery, images), new ControlFile()));
}

throw new Exception($"Request with key {parsedNamedQuery.StorageKey} not setup");
}
}
27 changes: 25 additions & 2 deletions src/protagonist/Orchestrator.Tests/Integration/NamedQueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
using System.Net.Http;
using DLCS.Core.Types;
using DLCS.Model.Assets;
using DLCS.Model.Assets.Metadata;
using DLCS.Model.Assets.NamedQueries;
using DLCS.Model.Policies;
using IIIF.Auth.V2;
using IIIF.ImageApi.V2;
using IIIF.ImageApi.V3;
Expand Down Expand Up @@ -274,6 +272,31 @@ public async Task Get_ReturnsV3ManifestWithCorrectCount_AsCanonical()
jsonResponse.SelectToken("items").Count().Should().Be(3);
}

[Theory]
[InlineData("iiif-resource/99/manifest-slash-test/with%2Fforward%2Fslashes/1")]
[InlineData("iiif-resource/99/manifest-slash-test/with%2fforward%2fslashes/1")]
public async Task Get_ReturnsManifestWithSlashes(string path)
{
// Arrange
dbFixture.DbContext.NamedQueries.Add(new NamedQuery
{
Customer = 99, Global = false, Id = Guid.NewGuid().ToString(), Name = "manifest-slash-test",
Template = "manifest=s1&canvas=n1&s1=p1&space=p2"
});

await dbFixture.DbContext.Images.AddTestAsset(AssetId.FromString("99/1/first"), num1: 1, ref1: "with/forward/slashes");
await dbFixture.DbContext.SaveChangesAsync();

// Act
var response = await httpClient.GetAsync(path);

// Assert
var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync());

jsonResponse.SelectToken("items").Count().Should().Be(1);
jsonResponse.SelectToken("items")[0].SelectToken("id").Value<string>().Should().Contain("99/1/first");
}

[Fact]
public async Task Get_ReturnsManifestWithCorrectlyOrderedItems()
{
Expand Down
Loading

0 comments on commit 9e1d84a

Please sign in to comment.