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

Allowing ability to create a collection #2

Merged
merged 24 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3c58a24
adding progress with creating collection + testing
JackLewis-digirati Aug 19, 2024
99de0d0
adding current create integration tests
JackLewis-digirati Aug 19, 2024
09ae61b
adding missing using
JackLewis-digirati Aug 20, 2024
ced0422
updates to add index + tests
JackLewis-digirati Aug 20, 2024
a74bcdf
changes to allow builds
JackLewis-digirati Aug 21, 2024
be9e238
adding test + AddCustomer for testing with auth
JackLewis-digirati Aug 21, 2024
b38801a
removing java from build
JackLewis-digirati Aug 21, 2024
5470977
change efcore version
JackLewis-digirati Aug 21, 2024
8f58bca
update build + add cleanup function
JackLewis-digirati Aug 21, 2024
ce6fa2a
re-add values to dotnet test
JackLewis-digirati Aug 21, 2024
50a9635
move dockerfile location
JackLewis-digirati Aug 21, 2024
e642df9
removing web
JackLewis-digirati Aug 21, 2024
fdf3ee0
update sdk + image name
JackLewis-digirati Aug 21, 2024
50ead5c
remove utlis
JackLewis-digirati Aug 21, 2024
4ea50a2
rename projects + remove DLCS refs
JackLewis-digirati Aug 21, 2024
c328bdb
remove unneeded file
JackLewis-digirati Aug 21, 2024
ee49cda
add changes pointed out by github
JackLewis-digirati Aug 21, 2024
9a84ce1
remove broken collection
JackLewis-digirati Aug 22, 2024
b2f44d5
removing GH warnings
JackLewis-digirati Aug 22, 2024
c51d633
second set of removal of warnings
JackLewis-digirati Aug 22, 2024
71d1575
fixing UrlRoot
JackLewis-digirati Aug 22, 2024
e6102da
more changes to remove warnings
JackLewis-digirati Aug 22, 2024
c76fd41
various changes for code review + move to using more of the iiif-net …
JackLewis-digirati Aug 23, 2024
846079c
remove bullseye-slim
JackLewis-digirati Aug 23, 2024
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
48 changes: 48 additions & 0 deletions .github/actions/docker-build-and-push/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Docker Build & Push
description: Composite GitHub Action to build and push Docker images to the DLCS GitHub Packages repositories.

inputs:
image-name:
description: "Name of the image to push to the GHCR repository."
required: true
dockerfile:
description: "The Dockerfile to build and push."
required: true
context:
description: "The context to use when building the Dockerfile."
required: true
github-token:
description: "The GitHub token used when interacting with GCHR."
required: true

runs:
using: "composite"
steps:
- id: checkout
uses: actions/checkout@v4
- id: docker-meta
uses: docker/metadata-action@v3
with:
images: ghcr.io/dlcs/${{ inputs.image-name }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,enable=true,prefix=,format=long
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- id: docker-login
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ inputs.github-token }}
- id: docker-build-push
uses: docker/build-push-action@v2
with:
context: ${{ inputs.context }}
file: ${{ inputs.dockerfile }}
builder: ${{ steps.docker-setup-buildx.outputs.name }}
tags: ${{ steps.docker-meta.outputs.tags }}
labels: ${{ steps.docker-meta.outputs.labels }}
push: ${{ github.actor != 'dependabot[bot]' }}
58 changes: 58 additions & 0 deletions .github/workflows/run_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: DLCS Build, Test & Publish
JackLewis-digirati marked this conversation as resolved.
Show resolved Hide resolved

on:
push:
branches: [ "main", "develop" ]
tags: [ "v*" ]
pull_request:
branches: [ "main", "develop" ]
paths-ignore:
- "docs/**"
- "scripts/**"
JackLewis-digirati marked this conversation as resolved.
Show resolved Hide resolved

jobs:
test-dotnet:
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/IIIFPresentation
env:
BUILD_CONFIG: "Release"
SOLUTION: "IIIFPresentation.sln"
steps:
- id: checkout
uses: actions/checkout@v4
- id: setup-dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
- id: restore-dotnet-dependencies
run: dotnet restore $SOLUTION
- id: build-dotnet
run: |
dotnet build $SOLUTION --configuration $BUILD_CONFIG --no-restore
dotnet test --configuration $BUILD_CONFIG --no-restore --no-build --verbosity normal

build-push-api:
runs-on: ubuntu-latest
needs: test-dotnet
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/docker-build-and-push
with:
image-name: "presentation-api"
dockerfile: "Dockerfile.API"
context: "./src/IIIFPresentation"
github-token: ${{ secrets.GITHUB_TOKEN }}

build-push-migrator:
runs-on: ubuntu-latest
needs: test-dotnet
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/docker-build-and-push
with:
image-name: "presentation-migrator"
dockerfile: "Dockerfile.Migrator"
context: "./src/IIIFPresentation"
github-token: ${{ secrets.GITHUB_TOKEN }}
28 changes: 28 additions & 0 deletions Dockerfile.API
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

COPY ["API/API.csproj", "API/"]
COPY ["Repository/Repository.csproj", "Repository/"]
COPY ["Models/Models.csproj", "Model/"]
COPY ["Core/Core.csproj", "Core/"]
COPY ["AWS/AWS.csproj", "AWS/"]

RUN dotnet restore "API/API.csproj"

COPY . .
WORKDIR "/src/API"
RUN dotnet build "API.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "API.csproj" -c Release -o /app/publish

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

LABEL maintainer="Donald Gray <[email protected]>,Tom Crane <[email protected]>, Jack Lewis <[email protected]>"
LABEL org.opencontainers.image.source=https://github.com/dlcs/iiif-presentation
LABEL org.opencontainers.image.description="HTTP API for iiif presentation interactions."

WORKDIR /app
EXPOSE 80
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "API.dll"]
28 changes: 28 additions & 0 deletions Dockerfile.Migrator
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

COPY ["Migrator/Migrator.csproj", "Migrator/"]
COPY ["Repository/Repository.csproj", "Repository/"]
COPY ["AWS/AWS.csproj", "AWS/"]
COPY ["Models/Models.csproj", "Model/"]
COPY ["Core/Core.csproj", "Core/"]

RUN dotnet restore "Migrator/Migrator.csproj"

COPY . .
WORKDIR "/src/Migrator"
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

LABEL maintainer="Donald Gray <[email protected]>, Jack Lewis <[email protected]>"
LABEL org.opencontainers.image.source=https://github.com/dlcs/iiif-presentation
LABEL org.opencontainers.image.description="EF Migration runner for iiif presentation DB"

WORKDIR /app
EXPOSE 80
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Migrator.dll"]
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using API.Converters;
using FluentAssertions;
using IIIF.Presentation.V3.Strings;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Models.API.Collection;
using Models.Database.Collections;
using Models.Response;

#nullable disable

namespace API.Tests.Converters;

Expand All @@ -28,7 +29,6 @@ public void ToHierarchicalCollection_ConvertsStorageCollection()
storageRoot.ToHierarchicalCollection(urlRoots, new EnumerableQuery<Collection>(CreateTestItems()));
// Assert
hierarchicalCollection.Id.Should().Be("http://base/1");
hierarchicalCollection.Type.Should().Be(PresentationType.Collection);
hierarchicalCollection.Label.Count.Should().Be(1);
hierarchicalCollection.Label["en"].Should().Contain("repository root");
hierarchicalCollection.Items.Count.Should().Be(1);
Expand All @@ -45,7 +45,6 @@ public void ToHierarchicalCollection_ConvertsStorageCollectionWithFullPath()
storageRoot.ToHierarchicalCollection(urlRoots, new EnumerableQuery<Collection>(CreateTestItems()));
// Assert
hierarchicalCollection.Id.Should().Be("http://base/1/top/some-id");
hierarchicalCollection.Type.Should().Be(PresentationType.Collection);
hierarchicalCollection.Label.Count.Should().Be(1);
hierarchicalCollection.Label["en"].Should().Contain("repository root");
hierarchicalCollection.Items.Count.Should().Be(1);
Expand All @@ -64,7 +63,6 @@ public void ToFlatCollection_ConvertsStorageCollection()
// Assert
flatCollection.Id.Should().Be("http://base/1/collections/some-id");
flatCollection.PublicId.Should().Be("http://base/1");
flatCollection.Type.Should().Be(PresentationType.Collection);
flatCollection.Label.Count.Should().Be(1);
flatCollection.Label["en"].Should().Contain("repository root");
flatCollection.Slug.Should().Be("root");
Expand All @@ -89,7 +87,6 @@ public void ToFlatCollection_ConvertsStorageCollection_WithFullPath()
// Assert
flatCollection.Id.Should().Be("http://base/1/collections/some-id");
flatCollection.PublicId.Should().Be("http://base/1/top/some-id");
flatCollection.Type.Should().Be(PresentationType.Collection);
flatCollection.Label.Count.Should().Be(1);
flatCollection.Label["en"].Should().Contain("repository root");
flatCollection.Slug.Should().Be("root");
Expand Down
129 changes: 129 additions & 0 deletions src/IIIFPresentation/API.Tests/Integration/CreateCollectionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using API.Tests.Integration.Infrastucture;
using Core.Response;
using FluentAssertions;
using IIIF.Presentation.V3.Strings;
using Microsoft.AspNetCore.Mvc.Testing;
using Models.API.Collection;
using Models.API.General;
using Models.Infrastucture;
using Repository;
using Test.Helpers.Integration;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace API.Tests.Integration;

[Trait("Category", "Integration")]
[Collection(CollectionDefinitions.DatabaseCollection.CollectionName)]
public class CreateCollectionTests : IClassFixture<PresentationAppFactory<Program>>
{
private readonly HttpClient httpClient;

private readonly PresentationContext dbContext;

private const int Customer = 1;

private readonly string parent;

public CreateCollectionTests(PresentationContextFixture dbFixture, PresentationAppFactory<Program> factory)
{
dbContext = dbFixture.DbContext;

httpClient = factory.WithConnectionString(dbFixture.ConnectionString)
.CreateClient(new WebApplicationFactoryClientOptions());

parent = dbContext.Collections.FirstOrDefault(x => x.CustomerId == Customer && x.Slug == string.Empty)!
.Id!;

dbFixture.CleanUp();
}

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

// Act
var response = await httpClient.AsCustomer(Customer).PostAsync($"{Customer}/collections",
new StringContent(JsonSerializer.Serialize(collection), Encoding.UTF8,
new MediaTypeHeaderValue("application/json")));

var responseCollection = await response.ReadAsPresentationResponseAsync<FlatCollection>();

var fromDatabase = dbContext.Collections.First(c => c.Id == responseCollection!.Id!.Split('/', StringSplitOptions.TrimEntries).Last());

// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
fromDatabase.Parent.Should().Be(parent);
fromDatabase.Label.Values.First()[0].Should().Be("test collection");
fromDatabase.Slug.Should().Be("programmatic-child");
fromDatabase.IsPublic.Should().BeTrue();
fromDatabase.IsStorageCollection.Should().BeTrue();
}

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

// Act
var response = await httpClient.AsCustomer(Customer).PostAsync($"{Customer}/collections",
new StringContent(JsonSerializer.Serialize(collection), Encoding.UTF8,
new MediaTypeHeaderValue("application/json")));

var error = await response.ReadAsPresentationResponseAsync<Error>();

// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
error!.Detail.Should().Be("The collection could not be created due to a duplicate slug value");
}

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

// Act
var response = await httpClient.PostAsync($"{Customer}/collections",
new StringContent(JsonSerializer.Serialize(collection), Encoding.UTF8,
new MediaTypeHeaderValue("application/json")));

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