Skip to content

Commit

Permalink
Add STAC API (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
tschumpr authored Nov 20, 2023
2 parents 302d243 + 42be7d0 commit 5407ebc
Show file tree
Hide file tree
Showing 15 changed files with 1,000 additions and 9 deletions.
32 changes: 31 additions & 1 deletion src/GeoCop.Api/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class Context : DbContext
/// <summary>
/// Database context to manage the database.
/// </summary>
/// <param name="options"></param>
/// <param name="options">Configuration options for the Context.</param>
public Context(DbContextOptions<Context> options)
: base(options)
{
Expand All @@ -32,11 +32,41 @@ public Context(DbContextOptions<Context> options)
/// </summary>
public DbSet<Delivery> Deliveries { get; set; }

/// <summary>
/// Gets the <see cref="Delivery"/> entity with all includes.
/// </summary>
public List<Delivery> DeliveriesWithIncludes
{
get
{
return Deliveries
.Include(d => d.DeliveryMandate)
.Include(d => d.Assets)
.AsNoTracking()
.ToList();
}
}

/// <summary>
/// Set of all <see cref="DeliveryMandate"/>.
/// </summary>
public DbSet<DeliveryMandate> DeliveryMandates { get; set; }

/// <summary>
/// Gets the <see cref="DeliveryMandate"/> entity with all includes.
/// </summary>
public List<DeliveryMandate> DeliveryMandatesWithIncludes
{
get
{
return DeliveryMandates
.Include(d => d.Deliveries)
.ThenInclude(d => d.Assets)
.AsNoTracking()
.ToList();
}
}

/// <summary>
/// Set of all <see cref="Asset"/>.
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions src/GeoCop.Api/Controllers/DeliveryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using GeoCop.Api.Contracts;
using GeoCop.Api.Models;
using GeoCop.Api.Validation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

Expand All @@ -11,6 +12,7 @@ namespace GeoCop.Api.Controllers
/// Controller for declaring deliveries.
/// </summary>
[ApiController]
[Authorize]
[Route("api/v{version:apiVersion}/[controller]")]
public class DeliveryController : ControllerBase
{
Expand Down
2 changes: 1 addition & 1 deletion src/GeoCop.Api/Controllers/DownloadController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public IActionResult Download(Guid jobId, string file)
}

var logFile = fileProvider.Open(file);
var contentType = contentTypeProvider.TryGetContentType(file, out var type) ? type : "application/octet-stream";
var contentType = contentTypeProvider.GetContentTypeAsString(file);
var logFileName = Path.GetFileNameWithoutExtension(validationJob.OriginalFileName) + "_log" + Path.GetExtension(file);
return File(logFile, contentType, logFileName);
}
Expand Down
3 changes: 3 additions & 0 deletions src/GeoCop.Api/GeoCop.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.14" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.14" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.*" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.11">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand All @@ -23,6 +24,8 @@
<PackageReference Include="Npgsql.NetTopologySuite" Version="7.0.6" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="DotNetStac.Api" Version="1.0.0-beta.5" />
</ItemGroup>

<ItemGroup>
Expand Down
52 changes: 52 additions & 0 deletions src/GeoCop.Api/IContentTypeProviderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using GeoCop.Api.Models;
using Microsoft.AspNetCore.StaticFiles;
using System.Net.Mime;

namespace GeoCop.Api
{
/// <summary>
/// Provides access to file content types.
/// </summary>
public static class IContentTypeProviderExtensions
{
private const string DefaultContentType = "application/octet-stream";

/// <summary>
/// Returns the <see cref="ContentType"/> for the specified <see cref="Asset"/>.
/// </summary>
/// <param name="contentTypeProvider">The IContentTypeProvider to extend.</param>
/// <param name="asset">The asset from which the content type should be read.</param>
/// <returns>The <see cref="ContentType"/>.</returns>
public static ContentType GetContentType(this IContentTypeProvider contentTypeProvider, Asset asset)
{
return contentTypeProvider.GetContentType(asset.OriginalFilename);
}

/// <summary>
/// Returns the <see cref="ContentType"/> for the specified file extension.
/// </summary>
/// <param name="contentTypeProvider">The IContentTypeProvider to extend.</param>
/// <param name="fileName">The file from which the content type should be read.</param>
/// <returns>The <see cref="ContentType"/>.</returns>
public static ContentType GetContentType(this IContentTypeProvider contentTypeProvider, string fileName)
{
return new ContentType(contentTypeProvider.GetContentTypeAsString(fileName));
}

/// <summary>
/// Returns the <see cref="ContentType"/> for the specified file extension.
/// </summary>
/// <param name="contentTypeProvider">The IContentTypeProvider to extend.</param>
/// <param name="fileName">The file from which the content type should be read.</param>
/// <returns>The content type as string.</returns>
public static string GetContentTypeAsString(this IContentTypeProvider contentTypeProvider, string fileName)
{
if (!contentTypeProvider.TryGetContentType(fileName, out var contentType))
{
contentType = DefaultContentType;
}

return contentType;
}
}
}
29 changes: 22 additions & 7 deletions src/GeoCop.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Asp.Versioning;
using GeoCop.Api;
using GeoCop.Api.StacServices;
using GeoCop.Api.Validation;
using GeoCop.Api.Validation.Interlis;
using Microsoft.AspNetCore.Authentication.JwtBearer;
Expand All @@ -15,13 +16,21 @@

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
// DotNetStac.Api uses the "All" policy for access in the STAC browser.
options.AddPolicy("All",
policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});

builder.Services
.AddControllers(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.AddJsonOptions(options =>
{
Expand Down Expand Up @@ -66,7 +75,7 @@

var contentTypeProvider = new FileExtensionContentTypeProvider();
contentTypeProvider.Mappings.TryAdd(".log", "text/plain");
contentTypeProvider.Mappings.TryAdd(".xtf", "text/xml; charset=utf-8");
contentTypeProvider.Mappings.TryAdd(".xtf", "application/interlis+xml");
builder.Services.AddSingleton<IContentTypeProvider>(contentTypeProvider);

builder.Services.AddSingleton<IValidationRunner, ValidationRunner>();
Expand All @@ -87,14 +96,18 @@
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
});

builder.Services.AddDbContext<Context>(options =>
var configureContextOptions = (DbContextOptionsBuilder options) =>
{
options.UseNpgsql(builder.Configuration.GetConnectionString("Context"), o =>
{
o.UseNetTopologySuite();
o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
});
});
};
builder.Services.AddDbContextFactory<Context>(configureContextOptions);
builder.Services.AddDbContext<Context>(configureContextOptions);

builder.Services.AddStacData(builder => { });

var app = builder.Build();

Expand All @@ -111,6 +124,8 @@

if (app.Environment.IsDevelopment())
{
app.UseCors("All");

if (!context.DeliveryMandates.Any())
context.SeedTestData();
}
Expand Down
56 changes: 56 additions & 0 deletions src/GeoCop.Api/StacServices/StacCollectionsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Microsoft.EntityFrameworkCore;
using Stac;
using Stac.Api.Interfaces;
using Stac.Api.WebApi.Implementations.Default;

namespace GeoCop.Api.StacServices
{
/// <summary>
/// Provides access to STAC collections.
/// </summary>
public class StacCollectionsProvider : ICollectionsProvider
{
private readonly ILogger<StacCollectionsProvider> logger;
private readonly IDbContextFactory<Context> contextFactory;
private readonly StacConverter stacConverter;

/// <summary>
/// Initializes a new instance of the <see cref="StacCollectionsProvider"/> class.
/// </summary>
public StacCollectionsProvider(ILogger<StacCollectionsProvider> logger, IDbContextFactory<Context> contextFactory, StacConverter stacConverter)
{
this.logger = logger;
this.contextFactory = contextFactory;
this.stacConverter = stacConverter;
}

/// <inheritdoc/>
public Task<StacCollection> GetCollectionByIdAsync(string collectionId, IStacApiContext stacApiContext, CancellationToken cancellationToken = default)
{
try
{
using var db = contextFactory.CreateDbContext();
var deliveryMandate = db.DeliveryMandatesWithIncludes.FirstOrDefault(dm => stacConverter.GetCollectionId(dm) == collectionId)
?? throw new InvalidOperationException($"Collection with id {collectionId} does not exist.");
var collection = stacConverter.ToStacCollection(deliveryMandate);
return Task.FromResult(collection);
}
catch (Exception ex)
{
var message = $"Error while getting collection with id {collectionId}";
logger.LogError(ex, message);
throw new InvalidOperationException(message, ex);
}
}

/// <inheritdoc/>
public Task<IEnumerable<StacCollection>> GetCollectionsAsync(IStacApiContext stacApiContext, CancellationToken cancellationToken = default)
{
using var db = contextFactory.CreateDbContext();
var collections = db.DeliveryMandatesWithIncludes.Select(stacConverter.ToStacCollection);
stacApiContext.Properties.SetProperty(DefaultConventions.MatchedCountPropertiesKey, collections.Count());

return Task.FromResult<IEnumerable<StacCollection>>(collections);
}
}
}
Loading

0 comments on commit 5407ebc

Please sign in to comment.