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

Support rejection of non-storage collections on create #12

Merged
merged 4 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using API.Features.Storage.Validators;
using FluentAssertions;
using FluentValidation.TestHelper;
using Models.API.Collection.Upsert;

namespace API.Tests.Features.Storage.Validation;

public class UpsertFlatCollectionValidatorTests
{
private readonly UpsertFlatCollectionValidator sut = new();

[Fact]
public void NewUpsertFlatCollectionValidator_MustHave_StorageCollection()
{
var upsertFlatCollection = new UpsertFlatCollection()
{
Parent = "parent",
Slug = "slug"
};

var result = sut.TestValidate(upsertFlatCollection);

result.IsValid.Should().BeFalse();
result.ShouldHaveValidationErrorFor(f => f.Behavior);
}
}
60 changes: 48 additions & 12 deletions src/IIIFPresentation/API.Tests/Integration/ModifyCollectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using IIIF.Presentation.V3.Strings;
using Microsoft.AspNetCore.Mvc.Testing;
using Models.API.Collection;
using Models.API.Collection.Update;
using Models.API.Collection.Upsert;
using Models.API.General;
using Models.Database.Collections;
using Models.Infrastucture;
Expand Down Expand Up @@ -45,7 +45,7 @@ public ModifyCollectionTests(PresentationContextFixture dbFixture, PresentationA
public async Task CreateCollection_CreatesCollection_WhenAllValuesProvided()
{
// Arrange
var collection = new FlatCollection()
var collection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand All @@ -54,7 +54,10 @@ public async Task CreateCollection_CreatesCollection_WhenAllValuesProvided()
},
Label = new LanguageMap("en", ["test collection"]),
Slug = "programmatic-child",
Parent = parent
Parent = parent,
Thumbnail = "some/thumbnail",
Tags = "some, tags",
ItemsOrder = 1,
};

var requestMessage = HttpRequestMessageBuilder.GetPrivateRequest(HttpMethod.Post, $"{Customer}/collections", JsonSerializer.Serialize(collection));
Expand All @@ -71,10 +74,37 @@ public async Task CreateCollection_CreatesCollection_WhenAllValuesProvided()
fromDatabase.Parent.Should().Be(parent);
fromDatabase.Label!.Values.First()[0].Should().Be("test collection");
fromDatabase.Slug.Should().Be("programmatic-child");
fromDatabase.ItemsOrder.Should().Be(1);
fromDatabase.Thumbnail.Should().Be("some/thumbnail");
fromDatabase.Tags.Should().Be("some, tags");
fromDatabase.IsPublic.Should().BeTrue();
fromDatabase.IsStorageCollection.Should().BeTrue();
}

[Fact]
public async Task CreateCollection_FailsToCreateCollection_WhenIsStorageCollectionFalse()
{
// Arrange
var collection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Behavior.IsPublic
},
Label = new LanguageMap("en", ["test collection"]),
Slug = "programmatic-child",
Parent = parent
};

var requestMessage = HttpRequestMessageBuilder.GetPrivateRequest(HttpMethod.Post, $"{Customer}/collections", JsonSerializer.Serialize(collection));

// Act
var response = await httpClient.AsCustomer(1).SendAsync(requestMessage);

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

[Fact]
public async Task CreateCollection_FailsToCreateCollection_WhenDuplicateSlug()
{
Expand Down Expand Up @@ -107,7 +137,7 @@ public async Task CreateCollection_FailsToCreateCollection_WhenDuplicateSlug()
public async Task CreateCollection_FailsToCreateCollection_WhenCalledWithoutAuth()
{
// Arrange
var collection = new FlatCollection()
var collection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand All @@ -132,7 +162,7 @@ public async Task CreateCollection_FailsToCreateCollection_WhenCalledWithoutAuth
public async Task CreateCollection_FailsToCreateCollection_WhenCalledWithIncorrectShowExtraHeader()
{
// Arrange
var collection = new FlatCollection()
var collection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand Down Expand Up @@ -160,7 +190,7 @@ public async Task CreateCollection_FailsToCreateCollection_WhenCalledWithIncorre
public async Task CreateCollection_FailsToCreateCollection_WhenCalledWithoutShowExtras()
{
// Arrange
var collection = new FlatCollection()
var collection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand Down Expand Up @@ -217,7 +247,7 @@ public async Task UpdateCollection_UpdatesCollection_WhenAllValuesProvided()

var getResponse = await httpClient.AsCustomer(1).SendAsync(getRequestMessage);

var updatedCollection = new UpdateFlatCollection()
var updatedCollection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand All @@ -226,7 +256,10 @@ public async Task UpdateCollection_UpdatesCollection_WhenAllValuesProvided()
},
Label = new LanguageMap("en", ["test collection - updated"]),
Slug = "programmatic-child",
Parent = parent
Parent = parent,
ItemsOrder = 1,
Thumbnail = "some/location/2",
Tags = "some, tags, 2",
};

var updateRequestMessage = HttpRequestMessageBuilder.GetPrivateRequest(HttpMethod.Put,
Expand All @@ -245,6 +278,9 @@ public async Task UpdateCollection_UpdatesCollection_WhenAllValuesProvided()
fromDatabase.Parent.Should().Be(parent);
fromDatabase.Label!.Values.First()[0].Should().Be("test collection - updated");
fromDatabase.Slug.Should().Be("programmatic-child");
fromDatabase.ItemsOrder.Should().Be(1);
fromDatabase.Thumbnail.Should().Be("some/location/2");
fromDatabase.Tags.Should().Be("some, tags, 2");
fromDatabase.IsPublic.Should().BeTrue();
fromDatabase.IsStorageCollection.Should().BeTrue();
}
Expand Down Expand Up @@ -283,7 +319,7 @@ public async Task UpdateCollection_UpdatesCollection_WhenAllValuesProvidedWithou

var getResponse = await httpClient.AsCustomer(1).SendAsync(getRequestMessage);

var updatedCollection = new UpdateFlatCollection()
var updatedCollection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand Down Expand Up @@ -348,7 +384,7 @@ public async Task UpdateCollection_FailsToUpdateCollection_WhenNotStorageCollect

var getResponse = await httpClient.AsCustomer(1).SendAsync(getRequestMessage);

var updatedCollection = new UpdateFlatCollection()
var updatedCollection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand All @@ -373,7 +409,7 @@ public async Task UpdateCollection_FailsToUpdateCollection_WhenNotStorageCollect
[Fact]
public async Task UpdateCollection_FailsToUpdateCollection_WhenETagIncorrect()
{
var updatedCollection = new UpdateFlatCollection()
var updatedCollection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand Down Expand Up @@ -404,7 +440,7 @@ public async Task UpdateCollection_FailsToUpdateCollection_WhenCalledWithoutNeed

var getResponse = await httpClient.AsCustomer(1).SendAsync(getRequestMessage);

var updatedCollection = new UpdateFlatCollection()
var updatedCollection = new UpsertFlatCollection()
{
Behavior = new List<string>()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@
using MediatR;
using Microsoft.Extensions.Options;
using Models.API.Collection;
using Models.API.Collection.Upsert;
using Models.Database.Collections;
using Repository;
using Repository.Helpers;

namespace API.Features.Storage.Requests;

public class CreateCollection(int customerId, FlatCollection collection, UrlRoots urlRoots)
public class CreateCollection(int customerId, UpsertFlatCollection collection, UrlRoots urlRoots)
: IRequest<ModifyEntityResult<FlatCollection>>
{
public int CustomerId { get; } = customerId;

public FlatCollection Collection { get; } = collection;
public UpsertFlatCollection Collection { get; } = collection;

public UrlRoots UrlRoots { get; } = urlRoots;
}
Expand All @@ -43,12 +44,12 @@ public async Task<ModifyEntityResult<FlatCollection>> Handle(CreateCollection re
CustomerId = request.CustomerId,
IsPublic = request.Collection.Behavior.IsPublic(),
IsStorageCollection = request.Collection.Behavior.IsStorageCollection(),
ItemsOrder = request.Collection.ItemsOrder,
JackLewis-digirati marked this conversation as resolved.
Show resolved Hide resolved
Label = request.Collection.Label,
Parent = request.Collection.Parent!.GetLastPathElement(),
Slug = request.Collection.Slug,
Thumbnail = request.Collection.Thumbnail,
Tags = request.Collection.Tags
Tags = request.Collection.Tags,
ItemsOrder = request.Collection.ItemsOrder
};

dbContext.Collections.Add(collection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Models.API.Collection;
using Models.API.Collection.Update;
using Models.API.Collection.Upsert;
using Repository;
using Repository.Helpers;

namespace API.Features.Storage.Requests;

public class UpdateCollection(int customerId, string collectionId, UpdateFlatCollection collection, UrlRoots urlRoots)
public class UpdateCollection(int customerId, string collectionId, UpsertFlatCollection collection, UrlRoots urlRoots)
: IRequest<ModifyEntityResult<FlatCollection>>
{
public int CustomerId { get; } = customerId;

public string CollectionId { get; set; } = collectionId;

public UpdateFlatCollection Collection { get; } = collection;
public UpsertFlatCollection Collection { get; } = collection;

public UrlRoots UrlRoots { get; } = urlRoots;
}
Expand Down Expand Up @@ -55,6 +55,7 @@ public async Task<ModifyEntityResult<FlatCollection>> Handle(UpdateCollection re
collectionFromDatabase.Slug = request.Collection.Slug;
collectionFromDatabase.Thumbnail = request.Collection.Thumbnail;
collectionFromDatabase.Tags = request.Collection.Tags;
collectionFromDatabase.ItemsOrder = request.Collection.ItemsOrder;

var saveErrors = await dbContext.TrySaveCollection(request.CustomerId, logger, cancellationToken);

Expand Down
13 changes: 7 additions & 6 deletions src/IIIFPresentation/API/Features/Storage/StorageController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using Microsoft.Extensions.Options;
using FluentValidation;
using Models.API.Collection;
using Models.API.Collection.Update;
using Models.API.Collection.Upsert;

namespace API.Features.Storage;

Expand Down Expand Up @@ -57,14 +57,15 @@ public async Task<IActionResult> Get(int customerId, string id)

[HttpPost("collections")]
[EtagCaching]
public async Task<IActionResult> Post(int customerId, [FromBody] FlatCollection collection, [FromServices] FlatCollectionValidator validator)
public async Task<IActionResult> Post(int customerId, [FromBody] UpsertFlatCollection collection,
[FromServices] UpsertFlatCollectionValidator validator)
{
if (!Request.ShowExtraProperties())
{
return Problem(statusCode: (int)HttpStatusCode.Forbidden);
}

var validation = await validator.ValidateAsync(collection, policy => policy.IncludeRuleSets("create"));
var validation = await validator.ValidateAsync(collection);

if (!validation.IsValid)
{
Expand All @@ -76,15 +77,15 @@ public async Task<IActionResult> Post(int customerId, [FromBody] FlatCollection

[HttpPut("collections/{id}")]
[EtagCaching]
public async Task<IActionResult> Put(int customerId, string id, [FromBody] UpdateFlatCollection collection,
[FromServices] UpdateFlatCollectionValidator validator)
public async Task<IActionResult> Put(int customerId, string id, [FromBody] UpsertFlatCollection collection,
[FromServices] UpsertFlatCollectionValidator validator)
{
if (!Request.ShowExtraProperties())
{
return Problem(statusCode: (int)HttpStatusCode.Forbidden);
}

var validation = await validator.ValidateAsync(collection, policy => policy.IncludeRuleSets("update"));
var validation = await validator.ValidateAsync(collection);

if (!validation.IsValid)
{
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using API.Features.Storage.Helpers;
using FluentValidation;
using Models.API.Collection.Upsert;

namespace API.Features.Storage.Validators;

public class UpsertFlatCollectionValidator : AbstractValidator<UpsertFlatCollection>
{
public UpsertFlatCollectionValidator()
{
RuleFor(f => f.Parent).NotEmpty().WithMessage("Requires a 'parent' to be set");
RuleFor(f => f.Slug).NotEmpty().WithMessage("Requires a 'slug' to be set");

RuleFor(f => f.Behavior).Must(f => f.IsStorageCollection())
.WithMessage("'Behavior' must contain 'storage-collection' when updating");
}
}
3 changes: 1 addition & 2 deletions src/IIIFPresentation/API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
});

builder.Services.AddScoped<FlatCollectionValidator>();
builder.Services.AddScoped<UpdateFlatCollectionValidator>();
builder.Services.AddScoped<UpsertFlatCollectionValidator>();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using IIIF.Presentation.V3.Strings;

namespace Models.API.Collection.Update;
namespace Models.API.Collection.Upsert;

public class UpdateFlatCollection
public class UpsertFlatCollection
{
public List<string> Behavior { get; set; } = new ();
public List<string> Behavior { get; set; } = [];

public LanguageMap? Label { get; set; }

Expand All @@ -15,4 +15,6 @@ public class UpdateFlatCollection
public string? Tags { get; set; }

public string? Thumbnail { get; set; }

public int? ItemsOrder { get; set; }
}
Loading