Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/run_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- id: setup-dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
dotnet-version: "10.0.x"
- id: restore-dotnet-dependencies
run: dotnet restore $SOLUTION
- id: build-dotnet
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.API
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

COPY ["API/API.csproj", "API/"]
Expand All @@ -15,7 +15,7 @@ RUN dotnet build "API.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "API.csproj" -c Release -r linux-x64 --self-contained false -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble AS base
ARG APP_VERSION=dev
ENV APP_VERSION=${APP_VERSION}

Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.BackgroundHandler
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

COPY ["BackgroundHandler/BackgroundHandler.csproj", "BackgroundHandler/"]
Expand All @@ -15,7 +15,7 @@ RUN dotnet build "BackgroundHandler.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "BackgroundHandler.csproj" -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble AS base

LABEL maintainer="Donald Gray <donald.gray@digirati.com>, Jack Lewis <jack.lewis@digirati.com>"
LABEL org.opencontainers.image.source=https://github.com/dlcs/iiif-presentation
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.Migrator
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

COPY ["Migrator/Migrator.csproj", "Migrator/"]
Expand All @@ -15,7 +15,7 @@ RUN dotnet build "Migrator.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Migrator.csproj" -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble AS base

LABEL maintainer="Donald Gray <donald.gray@digirati.com>, Jack Lewis <jack.lewis@digirati.com>"
LABEL org.opencontainers.image.source=https://github.com/dlcs/iiif-presentation
Expand Down
7 changes: 7 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sdk": {
"version": "10.0",
"rollForward": "latestMajor",
"allowPrerelease": false
}
}
18 changes: 9 additions & 9 deletions src/IIIFPresentation/API.Tests/API.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand All @@ -10,15 +10,15 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FakeItEasy" Version="8.3.0" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="FluentValidation" Version="11.10.0" />
<PackageReference Include="LateApexEarlySpeed.Xunit.Assertion.Json" Version="1.0.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="coverlet.collector" Version="10.0.1" />
<PackageReference Include="FakeItEasy" Version="9.0.1" />
<PackageReference Include="FluentAssertions" Version="7.2.2" />
<PackageReference Include="FluentValidation" Version="12.1.1" />
<PackageReference Include="LateApexEarlySpeed.Xunit.Assertion.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageReference Include="Stubbery" Version="2.8.3" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
22 changes: 11 additions & 11 deletions src/IIIFPresentation/API/API.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
Expand All @@ -9,22 +9,22 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.10.0" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
<PackageReference Include="iiif-net" Version="0.4.4" />
<PackageReference Include="JsonDiffPatch.Net" Version="2.5.0" />
<PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.11" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="8.0.11" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
<PackageReference Include="Sqids" Version="3.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="10.0.11" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="10.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.9.0" />
<PackageReference Include="Sqids" Version="3.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public async Task<ParsedManifestResult> GenerateCanvasPaintings(
var manifestParseResult = await ParseManifest(customerId, presentationManifest);
if (manifestParseResult.Error != null) return ParsedManifestResult.Failure(manifestParseResult.Error);

Debug.Assert(manifestParseResult.CanvasPaintings is not null, "manifestParseResult.CanvasPaintings is not null");
Debug.Assert(manifestParseResult.CanvasPaintings is not null);

var insertCanvasPaintingsError = await HandleInserts(manifestParseResult.CanvasPaintings, customerId, cancellationToken);
if (insertCanvasPaintingsError != null) return ParsedManifestResult.Failure(insertCanvasPaintingsError);
Expand Down Expand Up @@ -71,7 +71,7 @@ public async Task<ParsedManifestResult> UpdateCanvasPaintings(int customerId, Pr
if (manifestParseResult.Error != null) return ParsedManifestResult.Failure(manifestParseResult.Error);

existingManifest.CanvasPaintings ??= [];
Debug.Assert(manifestParseResult.CanvasPaintings is not null, "manifestParseResult.CanvasPaintings is not null");
Debug.Assert(manifestParseResult.CanvasPaintings is not null);

var toInsert = UpdateCanvasPaintingRecords(existingManifest.CanvasPaintings,
manifestParseResult.CanvasPaintings, existingManifest.SpaceId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public async Task<FetchEntityResult<PresentationManifest>> GetManifest(int custo
// or if not found in "staging", an error was logged and we fall back to "real"
manifest ??= await iiifS3.ReadIIIFFromS3<PresentationManifest>(dbManifest, BucketLocationType.Default, cancellationToken);

dbManifest.Hierarchy.Single().FullPath = await fetchFullPath;
dbManifest.Hierarchy!.Single().FullPath = await fetchFullPath;

if (manifest == null)
return FetchEntityResult<PresentationManifest>.Failure(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ private async Task<PresentationResult> GeneratePresentationSuccessResult(Present
presentationManifest.SetGeneratedFields(dbManifest, pathGenerator, savedManifestPathGenerator, assets,
finishedPipelinesLimit: options.Value.FinishedPipelinesLimit),
writeResult,
dbManifest?.Etag);
dbManifest.Etag);
}

private async Task<(PresentationResult?, DbManifest?)> CreateDatabaseRecord(WriteManifestRequest request,
Expand Down Expand Up @@ -414,7 +414,7 @@ private async Task<PresentationResult> GeneratePresentationSuccessResult(Present

if (saveErrors != null) return saveErrors;

dbManifest.Hierarchy.Single().FullPath =
dbManifest.Hierarchy!.Single().FullPath =
await ManifestRetrieval.RetrieveFullPathForManifest(dbManifest.Id, dbManifest.CustomerId, dbContext,
cancellationToken);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ private async Task<PresentationResult> UpdateInternal(UpsertCollectionRequest re
var saveErrors = await dbContext.TrySave("collection", request.CustomerId, logger, cancellationToken);
if (saveErrors != null) return saveErrors;

var hierarchy = databaseCollection.Hierarchy.Single();
var hierarchy = databaseCollection.Hierarchy!.Single();
if (hierarchy.Parent != null)
{
var fullPathError = await TrySetFullPath(databaseCollection, hierarchy, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Core.Helpers;
using IIIF.Presentation;
using Models.API.Collection;
using Services.Manifests.Helpers;

namespace API.Features.Storage.Helpers;

Expand All @@ -17,7 +18,7 @@ public static class HierarchicalCollectionResponse
/// collections - preserving any custom behaviors that the write service's enriched entity would otherwise have
/// discarded (see <see cref="API.Converters.CollectionConverter.EnrichPresentationCollection"/>). The response
/// id is taken from the enriched entity's <c>PublicId</c>, which already accounts for customers with a
/// configured <see cref="Repository.Paths.SettingsBasedPathGenerator"/> path.
/// configured <see cref="SettingsBasedPathGenerator"/> path.
/// </summary>
/// <param name="result">Result of the underlying <see cref="ICollectionWrite"/> call</param>
/// <param name="rawRequestBody">The raw request body, re-parsed for non-storage collections</param>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using API.Features.Common.Helpers;
using API.Features.Storage.Helpers;
using Core;
using MediatR;
using Models;
using Models.API.General;
using Models.Database.Collections;
using Repository;

namespace API.Features.Storage.Requests;

Expand Down
3 changes: 1 addition & 2 deletions src/IIIFPresentation/API/Helpers/CollectionHelperX.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using API.Infrastructure.Validation;
using Models.API.General;
using Models.API.General;
using Models.Database.Collections;

namespace API.Helpers;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using API.Infrastructure.Requests;
using Core.Web;
using DLCS;
using DLCS;
using Microsoft.Extensions.Options;
using Repository.Paths;

Expand Down
1 change: 1 addition & 0 deletions src/IIIFPresentation/API/Helpers/ParentSlugParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public interface IParentSlugParser
/// Slug for the resource - for hierarchical PUT, the last segment of the path; for flat requests, derived from
/// the body's "id" property when it resolves to an own-host hierarchical id
/// </param>
/// <param name="cancellationToken">Current cancellation token</param>
public Task<ParsedParentSlugResult> Parse<T>(
T presentation,
int customerId,
Expand Down
3 changes: 1 addition & 2 deletions src/IIIFPresentation/API/Helpers/PresentationX.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using API.Infrastructure.Validation;
using Core.Helpers;
using Core.Helpers;
using Models.API;
using Models.API.General;

Expand Down
11 changes: 5 additions & 6 deletions src/IIIFPresentation/API/Infrastructure/ControllerBaseX.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Net;
using System.Runtime.InteropServices.JavaScript;
using API.Features.Storage.Helpers;
using API.Infrastructure.Http;
using API.Infrastructure.Requests;
Expand Down Expand Up @@ -63,9 +62,9 @@ public static IActionResult FetchResultToHttpResult<T>(this ControllerBase contr
/// </summary>
/// <param name="controller">Current controllerBase object</param>
/// <param name="entityResult">Result to transform</param>
/// <param name="instance">The value for <see cref="JSType.Error.Instance" />.</param>
/// <param name="instance">The value for <see cref="Error.Instance" />.</param>
/// <param name="errorTitle">
/// The value for <see cref="JSType.Error.Title" />. In some instances this will be prepended to the actual error name.
/// The value for <see cref="Error.Title" />. In some instances this will be prepended to the actual error name.
/// e.g. errorTitle + ": Conflict"
/// </param>
/// <returns>
Expand All @@ -77,11 +76,11 @@ public static IActionResult ModifyResultToHttpResult(this ControllerBase control
string? errorTitle) =>
entityResult.WriteResult switch
{
WriteResult.Updated => controller.PresentationContent(entityResult.Entity, etag: entityResult.ETag),
WriteResult.Updated => controller.PresentationContent(entityResult.Entity!, etag: entityResult.ETag),
WriteResult.Accepted => controller.PresentationWithLocationHeader(controller.Request.GetDisplayUrl(),
entityResult.Entity, (int)HttpStatusCode.Accepted, null),
entityResult.Entity!, (int)HttpStatusCode.Accepted, null),
WriteResult.Created => controller.PresentationWithLocationHeader(controller.Request.GetDisplayUrl(),
entityResult.Entity, (int)HttpStatusCode.Created, entityResult.ETag),
entityResult.Entity!, (int)HttpStatusCode.Created, entityResult.ETag),
WriteResult.NotFound => controller.PresentationNotFound(entityResult.Error),
WriteResult.Error => controller.PresentationProblem(entityResult.Error, instance,
(int)HttpStatusCode.InternalServerError, errorTitle, controller.GetErrorType(entityResult.ErrorType)),
Expand Down
1 change: 0 additions & 1 deletion src/IIIFPresentation/API/Infrastructure/ETagCache.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using API.Features.Manifest.Requests;
using API.Settings;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
Expand Down
71 changes: 37 additions & 34 deletions src/IIIFPresentation/API/Infrastructure/Helpers/HttpRequestX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,48 @@ public static class HttpRequestX
private static readonly KeyValuePair<string, string> AdditionalPropertiesHeader = new (CustomHttpHeaders.ShowExtras, "All");
private const string CreateSpaceHeader = "<https://dlcs.io/vocab#Space>;rel=\"DCTERMS.requires\"";

/// <summary>
/// Checks if the <see cref="HttpRequest"/> has appropriate header to show additional parameters
/// </summary>
public static bool HasShowExtraHeader(this HttpRequest request)
{
return request.Headers.FirstOrDefault(h => string.Equals(h.Key, AdditionalPropertiesHeader.Key, StringComparison.OrdinalIgnoreCase)).Value ==
AdditionalPropertiesHeader.Value;
}

/// <summary>
/// Checks if the <see cref="HttpRequest"/> has header requesting a space be created
/// </summary>
public static bool HasCreateSpaceHeader(this HttpRequest request)
=> request.Headers.Link.Contains(CreateSpaceHeader);

/// <summary>
/// Retrieve the customer id
///
/// NOTE: retrieved from route values
/// </summary>
/// <param name="request">The request to get the customer id from</param>
/// <returns>A parsed customer id</returns>
public static int? GetCustomerId(this HttpRequest request, ILogger logger)
extension(HttpRequest request)
{
var customerIdRouteValue = "customerId";

if (!request.RouteValues.TryGetValue(customerIdRouteValue, out var customerIdRouteVal)
|| customerIdRouteVal is null)
/// <summary>
/// Checks if the <see cref="HttpRequest"/> has appropriate header to show additional parameters
/// </summary>
public bool HasShowExtraHeader()
{
logger.LogDebug("Unable to identify customerId in auth request to {Path}", request.Path);
return null;
return request.Headers.FirstOrDefault(h => string.Equals(h.Key, AdditionalPropertiesHeader.Key, StringComparison.OrdinalIgnoreCase)).Value ==
AdditionalPropertiesHeader.Value;
}

if (!int.TryParse(customerIdRouteVal.ToString(), out int customerId))

/// <summary>
/// Checks if the <see cref="HttpRequest"/> has header requesting a space be created
/// </summary>
public bool HasCreateSpaceHeader()
=> request.Headers.Link.Contains(CreateSpaceHeader);

/// <summary>
/// Retrieve the customer id
///
/// NOTE: retrieved from route values
/// </summary>
/// <returns>A parsed customer id</returns>
public int? GetCustomerId(ILogger logger)
{
logger.LogDebug("Specified customerId is not numeric {Path}", request.Path);
return null;
}
const string customerIdRouteValue = "customerId";

return customerId;
if (!request.RouteValues.TryGetValue(customerIdRouteValue, out var customerIdRouteVal)
|| customerIdRouteVal is null)
{
logger.LogDebug("Unable to identify customerId in auth request to {Path}", request.Path);
return null;
}

if (!int.TryParse(customerIdRouteVal.ToString(), out int customerId))
{
logger.LogDebug("Specified customerId is not numeric {Path}", request.Path);
return null;
}

return customerId;
}
}
}
Loading
Loading