Skip to content

Commit

Permalink
Presrvation id generation
Browse files Browse the repository at this point in the history
  • Loading branch information
donaldgray committed Apr 30, 2024
1 parent 1de8b49 commit cbec168
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 112 deletions.
2 changes: 1 addition & 1 deletion LeedsExperiment/Fedora/Abstractions/Resource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ protected Resource()
[JsonPropertyName("@id")]
[JsonPropertyOrder(1)]
// The URI for this Storage API (accessible to API consumers)
public Uri? PreservationApiUri { get; set; }
public Uri? StorageApiUri { get; set; }

[JsonPropertyName("id")]
[JsonPropertyOrder(12)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Preservation.API.Models;

namespace Preservation.API.Controllers;

[Route("[controller]/{*path}")]
[ApiController]
public class RepositoryController(IPreservation preservation) : Controller
public class RepositoryController(IPreservation preservation, ModelConverter modelConverter) : Controller
{
/// <summary>
/// Browse underlying repository for Container, DigitalObject or Binary.
/// </summary>
/// <returns></returns>
[HttpGet]
/// <param name="path">Path to item in repository to fetch (e.g. path/to/item</param>
/// <param name="version">
/// Optional version parameter, in format "v1","v2" etc. Current version returned if omitted
/// </param>
/// <returns><see cref="Container"/>, <see cref="DigitalObject"/> or <see cref="Binary"/></returns>
[HttpGet(Name = "Browse")]
[Produces("application/json")]
public async Task<IActionResult> Index([FromRoute] string path, [FromQuery] string? version = null)
{
// How do we know if this is an archive group or not?
var unEscapedPath = Uri.UnescapeDataString(path);
var storageResource = string.IsNullOrEmpty(version)
? await preservation.GetResource(path)
: await preservation.GetArchivalGroup(path, version);
? await preservation.GetResource(unEscapedPath)
: await preservation.GetArchivalGroup(unEscapedPath, version);

if (storageResource == null) return NotFound();
// Convert to appropriate type
var preservationResource = storageResource.ToPreservationResource(new Uri(HttpContext.Request.GetDisplayUrl()));

var preservationResource =
modelConverter.ToPreservationResource(storageResource, new Uri(HttpContext.Request.GetDisplayUrl()));
return Ok(preservationResource);
}
}
93 changes: 0 additions & 93 deletions LeedsExperiment/Preservation.API/ModelConverters.cs

This file was deleted.

88 changes: 88 additions & 0 deletions LeedsExperiment/Preservation.API/Models/ModelConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using Fedora.Abstractions;
using Fedora.Storage;

namespace Preservation.API.Models;

public class ModelConverter(UriGenerator uriGenerator)
{
public PreservationResource ToPreservationResource(Fedora.Abstractions.Resource storageResource, Uri requestPath)
{
switch (storageResource)
{
case Fedora.Abstractions.ArchivalGroup ag:
{
var digitalObject = new DigitalObject
{
Id = requestPath,
Name = ag.Name,
Version = ToDigitalObjectVersion(ag.Version, requestPath),
Versions = (ag.Versions ?? Array.Empty<ObjectVersion>())
.Select(v => ToDigitalObjectVersion(v, requestPath)!).ToArray(),
Binaries = ag.Binaries.Select(b => ToPresentationBinary(b)).ToArray(),
Containers = ag.Containers.Select(c => ToPresentationContainer(c)).ToArray(),
};
MapBasicsFromStorageResource(digitalObject, storageResource);
return digitalObject;
}
case Fedora.Abstractions.Container c:
return ToPresentationContainer(c);
case Fedora.Abstractions.Binary b:
return ToPresentationBinary(b);
}

throw new InvalidOperationException($"Unable to handle {storageResource.GetType()} resource");
}

private DigitalObjectVersion? ToDigitalObjectVersion(ObjectVersion? objectVersion, Uri repositoryUri)
{
if (objectVersion == null) return null;

var digitalObjectVersion = new DigitalObjectVersion
{
Id = uriGenerator.GetRepositoryPath(repositoryUri, objectVersion.OcflVersion),
Name = objectVersion.OcflVersion,
Date = objectVersion.MementoDateTime,
};
return digitalObjectVersion;
}

private Binary ToPresentationBinary(Fedora.Abstractions.Binary fedoraBinary)
{
var binary = new Binary
{
Id = uriGenerator.GetRepositoryPath(fedoraBinary.StorageApiUri),
Content = new Uri("https://todo"),
Name = fedoraBinary.FileName,
Digest = fedoraBinary.Digest,
Location = new Uri("s3://todo/path"),
PartOf = uriGenerator.GetRepositoryPath(fedoraBinary.PartOf),
};

MapBasicsFromStorageResource(binary, fedoraBinary);
return binary;
}

private Container ToPresentationContainer(Fedora.Abstractions.Container fedoraContainer)
{
var container = new Container
{
Id = uriGenerator.GetRepositoryPath(fedoraContainer.StorageApiUri),
Name = fedoraContainer.Name,
Containers = fedoraContainer.Containers.Select(c => ToPresentationContainer(c)).ToArray(),
Binaries = fedoraContainer.Binaries.Select(b => ToPresentationBinary(b)).ToArray(),
PartOf = uriGenerator.GetRepositoryPath(fedoraContainer.PartOf),
};

MapBasicsFromStorageResource(container, fedoraContainer);
return container;
}

private void MapBasicsFromStorageResource<T>(T target, Resource resource)
where T : PreservationResource, new()
{
target.Created = resource.Created ?? DateTime.MinValue;
target.CreatedBy = new Uri($"http://example.id/{resource.CreatedBy}");
target.LastModified = resource.LastModified;
target.LastModifiedBy = new Uri($"http://example.id/{resource.LastModifiedBy}");
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;

namespace Preservation.API;
namespace Preservation.API.Models;

/// <summary>
/// Base class for Preservation API models
Expand Down
36 changes: 36 additions & 0 deletions LeedsExperiment/Preservation.API/Models/UriGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Diagnostics.CodeAnalysis;

namespace Preservation.API.Models;

public class UriGenerator(IHttpContextAccessor httpContextAccessor)
{
[return: NotNullIfNotNull(nameof(storageUri))]
public Uri? GetRepositoryPath(Uri? storageUri, string? version = null)
{
if (storageUri == null) return null;

// Only append ?version if provided as arg, so use AbsolutePath only be default
var request = httpContextAccessor.HttpContext.Request;
var uriBuilder = new UriBuilder(request.Scheme, request.Host.Host)
{
Port = request.Host.Port ?? 80,
Path = GetPreservationPath(storageUri)
};

if (!string.IsNullOrEmpty(version))
{
uriBuilder.Query = $"version={version}";
}

return uriBuilder.Uri;
}

private static string GetPreservationPath(Uri storageUri)
{
var storageUriAbsolutePath = storageUri.AbsolutePath;
const string storageApiPrefix = "/api";
return Uri.UnescapeDataString(storageUriAbsolutePath[..4] == storageApiPrefix
? storageUriAbsolutePath[4..]
: storageUriAbsolutePath);
}
}
11 changes: 8 additions & 3 deletions LeedsExperiment/Preservation.API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Text.Json.Serialization;
using Microsoft.OpenApi.Models;
using Preservation;
using Preservation.API.Models;
using PreservationApiClient;

var builder = WebApplication.CreateBuilder(args);
Expand All @@ -12,17 +13,21 @@
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault;
});

builder.Services.AddHttpLogging(o => { });

builder.Services.AddHttpClient<IPreservation, StorageService>(client =>
{
client.BaseAddress = new Uri(builder.Configuration["StorageApiBaseAddress"]!);
client.DefaultRequestHeaders.Add("Accept", "application/json");
});

builder.Services
.AddHttpContextAccessor()
.AddHttpLogging(o => { })
.AddScoped<UriGenerator>()
.AddScoped<ModelConverter>();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(opts =>
{
Expand Down
10 changes: 5 additions & 5 deletions LeedsExperiment/Preservation/FedoraWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,15 @@ private Container MakeContainer(bool isArchivalGroup, FedoraJsonLdResponse conta
return new ArchivalGroup(containerResponse)
{
ObjectPath = GetObjectPath(containerResponse.Id),
PreservationApiUri = GetApiUri(containerResponse.Id)
StorageApiUri = GetApiUri(containerResponse.Id)
};
}
else
{
return new Container(containerResponse)
{
ObjectPath = GetObjectPath(containerResponse.Id),
PreservationApiUri = GetApiUri(containerResponse.Id)
StorageApiUri = GetApiUri(containerResponse.Id)
};
}
}
Expand All @@ -177,7 +177,7 @@ private Binary MakeBinary(BinaryMetadataResponse binaryResponse)
return new Binary(binaryResponse)
{
ObjectPath = GetObjectPath(binaryResponse.Id),
PreservationApiUri = GetApiUri(binaryResponse.Id)
StorageApiUri = GetApiUri(binaryResponse.Id)
};
}

Expand Down Expand Up @@ -902,7 +902,7 @@ private List<string> GetIdsFromContainsProperty(JsonElement element)
container = new Container(fedoraContainer)
{
ObjectPath = GetObjectPath(fedoraContainer.Id),
PreservationApiUri = GetApiUri(fedoraContainer.Id)
StorageApiUri = GetApiUri(fedoraContainer.Id)
};
}
topContainer.Containers.Add(container!);
Expand All @@ -913,7 +913,7 @@ private List<string> GetIdsFromContainsProperty(JsonElement element)
var binary = new Binary(fedoraBinary!)
{
ObjectPath = GetObjectPath(fedoraBinary!.Id),
PreservationApiUri = GetApiUri(fedoraBinary.Id)
StorageApiUri = GetApiUri(fedoraBinary.Id)
};
topContainer.Binaries.Add(binary);
}
Expand Down

0 comments on commit cbec168

Please sign in to comment.