Compare commits

5 Commits

7 changed files with 106 additions and 132 deletions

View File

@@ -58,7 +58,7 @@ namespace AS1024.GeoFeed.Core.GeoFeedSqliteLocalCache
List<IPGeoFeed> cachedData = [];
results.ForEach(cachedData.Add);
return cachedData.ToGeoFeedCsv();
return cachedData.ToGeoFeedCsv(true, true);
}
private async Task DBContextMigrate()

View File

@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>

View File

@@ -0,0 +1,70 @@
using System.Text;
using AS1024.GeoFeed.Core.Interfaces;
using AS1024.GeoFeed.Core.Tools;
using AS1024.GeoFeed.Models;
using Microsoft.Extensions.Caching.Memory;
namespace AS1024.GeoFeed.Core.GeoFeedLogic
{
public class GeoFeedReturn
{
private const string GeoFeedCacheKey = "GeoFeedData";
private readonly IGeoFeedProvider provider;
private readonly ILogger<GeoFeedReturn> logger;
private readonly IGeoFeedPersistentCacheProvider cacheProvider;
private readonly IMemoryCache memoryCache;
private readonly IWebHostEnvironment environment;
public GeoFeedReturn(IGeoFeedProvider provider,
ILogger<GeoFeedReturn> logger,
IGeoFeedPersistentCacheProvider cacheProvider,
IMemoryCache memoryCache,
IWebHostEnvironment environment)
{
this.provider = provider;
this.logger = logger;
this.cacheProvider = cacheProvider;
this.memoryCache = memoryCache;
this.environment = environment;
}
public async Task<IResult> GetGeoFeed()
{
bool isCached = true;
try
{
if (!memoryCache.TryGetValue(GeoFeedCacheKey, out List<IPGeoFeed>? feed))
{
isCached = false;
feed = await provider.GetGeoFeedData();
if (environment.IsProduction())
{
MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(15));
memoryCache.Set(GeoFeedCacheKey, feed, cacheEntryOptions);
}
}
return Results.File(Encoding.UTF8.GetBytes(feed.ToGeoFeedCsv(true, isCached)),
"text/csv",
"geofeed.csv");
}
catch (HttpRequestException ex)
{
logger.LogWarning($"Temporary failure of retrieving GeoData from upstream. {ex}");
string geoFeedData = cacheProvider.GetGeoFeed();
return Results.File(Encoding.UTF8.GetBytes(geoFeedData),
"text/csv",
"geofeed.csv");
}
catch (Exception ex)
{
logger.LogError($"Error: {ex}");
}
return Results.NoContent();
}
}
}

View File

@@ -5,10 +5,23 @@ namespace AS1024.GeoFeed.Core.Tools
{
public static class GeoFeedTools
{
public static string ToGeoFeedCsv(this List<IPGeoFeed> geoFeeds)
/// <summary>
/// Returns a CSV string for a given GeoFeed retreived from various sources
/// </summary>
/// <param name="geoFeeds">GeoFeed returned from the source of truth</param>
/// <param name="timeStamp">If a timestamp should be appended at the header</param>
/// <param name="isCached">If the result is cached</param>
/// <returns></returns>
public static string ToGeoFeedCsv(this List<IPGeoFeed> geoFeeds, bool timeStamp = false, bool isCached = false)
{
StringBuilder csvContent = new();
if (timeStamp)
csvContent.AppendLine($"# GeoFeed generated on {DateTime.UtcNow:R}");
if (isCached)
csvContent.AppendLine($"# Geofeed data is returned from local in memory cache");
foreach (IPGeoFeed feed in geoFeeds)
{
csvContent.AppendLine($"{feed.Prefix},{feed.GeolocCountry},{feed.GeolocRegion},{feed.GeolocCity},{feed.GeolocPostalCode}");

View File

@@ -1,14 +1,10 @@
using AS1024.GeoFeed.Core.CacheService;
using AS1024.GeoFeed.Core.GeoFeedProviders;
using AS1024.GeoFeed.Core.GeoFeedLogic;
using AS1024.GeoFeed.Core.GeoFeedPreloader;
using AS1024.GeoFeed.Core.Interfaces;
using AS1024.GeoFeed.Core.Tools;
using AS1024.GeoFeed.Models;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Text;
namespace AS1024.GeoFeed.MinimalAPI
@@ -21,7 +17,9 @@ namespace AS1024.GeoFeed.MinimalAPI
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddTransient<IGeoFeedProvider, NetboxAoTGeoFeedProvider>();
builder.Services.AddHostedService<GeoFeedCacheService>();
builder.Services.AddHostedService<PreLoadGeoFeed>();
builder.Services.AddTransient<IGeoFeedPersistentCacheProvider, GeoFeedLocalFileCache>();
builder.Services.AddScoped<GeoFeedReturn>();
builder.Services.AddMemoryCache();
builder.Services.AddLogging();
builder.Services.AddHttpClient();
@@ -30,64 +28,15 @@ namespace AS1024.GeoFeed.MinimalAPI
});
var app = builder.Build();
app.Map("/geofeed.csv", async (IGeoFeedProvider provider,
ILogger<Program> logger,
IGeoFeedPersistentCacheProvider cacheProvider,
IMemoryCache memoryCache,
IWebHostEnvironment environment) => {
return await GeoFeedDataRunner(provider, logger, cacheProvider, memoryCache, environment);
});
app.Map("/geofeed.csv", async (GeoFeedReturn feedReturn) => {
return await feedReturn.GetGeoFeed();
});
app.Map("/geofeed", async (IGeoFeedProvider provider,
ILogger<Program> logger,
IGeoFeedPersistentCacheProvider cacheProvider,
IMemoryCache memoryCache,
IWebHostEnvironment environment) => {
return await GeoFeedDataRunner(provider, logger, cacheProvider, memoryCache, environment);
});
app.Map("/geofeed", async (GeoFeedReturn feedReturn) => {
return await feedReturn.GetGeoFeed();
});
app.Run();
}
protected static async Task<IResult> GeoFeedDataRunner(IGeoFeedProvider provider,
ILogger<Program> logger,
IGeoFeedPersistentCacheProvider cacheProvider,
IMemoryCache memoryCache,
IWebHostEnvironment environment)
{
try
{
if (!memoryCache.TryGetValue("Geofeed", out List<IPGeoFeed>? feed))
{
feed = await provider.GetGeoFeedData();
if (environment.IsProduction())
{
MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(15));
memoryCache.Set("Geofeed", feed, cacheEntryOptions);
}
}
return Results.File(Encoding.UTF8.GetBytes(feed.ToGeoFeedCsv()),
"text/csv",
"geofeed.csv");
}
catch (HttpRequestException ex)
{
logger.LogWarning($"Temporary failure of retrieving GeoData from upstream. {ex}");
string geoFeedData = cacheProvider.GetGeoFeed();
return Results.File(Encoding.UTF8.GetBytes(geoFeedData),
"text/csv",
"geofeed.csv");
}
catch (Exception ex)
{
logger.LogError($"Error: {ex}");
}
return Results.NoContent();
}
}
}

View File

@@ -1,9 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using AS1024.GeoFeed.Core.Interfaces;
using Microsoft.Extensions.Caching.Memory;
using AS1024.GeoFeed.Models;
using System.Text;
using AS1024.GeoFeed.Core.Tools;
using AS1024.GeoFeed.Core.GeoFeedLogic;
namespace AS1024.GeoFeed.Controllers
{
@@ -13,75 +9,18 @@ namespace AS1024.GeoFeed.Controllers
public class GeofeedController : ControllerBase
{
private readonly IGeoFeedProvider builder;
private readonly IMemoryCache memoryCache;
private readonly IWebHostEnvironment environment;
private readonly ILogger<GeofeedController> logger;
private readonly IGeoFeedPersistentCacheProvider geoFeedPersistentCache;
private const string GeoFeedCacheKey = "GeoFeedData";
private readonly GeoFeedReturn feedReturn;
public GeofeedController(IGeoFeedProvider builder,
IMemoryCache memoryCache,
IWebHostEnvironment environment,
ILogger<GeofeedController> logger,
IGeoFeedPersistentCacheProvider geoFeedPersistentCache) {
this.logger = logger;
this.geoFeedPersistentCache = geoFeedPersistentCache;
this.builder = builder;
this.memoryCache = memoryCache;
this.environment = environment;
public GeofeedController(GeoFeedReturn feedReturn)
{
this.feedReturn = feedReturn;
}
[HttpGet]
[Route("")]
public async Task<IActionResult> Get()
public async Task<IResult> Get()
{
try
{
if (!memoryCache.TryGetValue(GeoFeedCacheKey, out List<IPGeoFeed>? feed))
{
feed = await builder.GetGeoFeedData();
if (environment.IsProduction())
{
MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(15));
memoryCache.Set(GeoFeedCacheKey, feed, cacheEntryOptions);
}
}
return ReturnFile(feed);
} catch (HttpRequestException ex)
{
logger.LogWarning($"Temporary failure of retrieving GeoData from upstream. {ex}");
var cachedData = geoFeedPersistentCache.GetGeoFeed();
return ReturnFile(cachedData);
}
catch (Exception ex)
{
logger.LogError($"Geofeed generation failed. Exception: {ex}");
return StatusCode(500);
}
}
[NonAction]
private IActionResult ReturnFile(List<IPGeoFeed>? feed)
{
string csvContent = feed.ToGeoFeedCsv(); // Assuming ToGeoFeedCsv() returns a string in CSV format.
return ReturnFile(csvContent);
}
[NonAction]
private IActionResult ReturnFile(string csvContent)
{
byte[] contentBytes = Encoding.UTF8.GetBytes(csvContent);
string contentType = "text/csv";
return new FileContentResult(contentBytes, contentType)
{
FileDownloadName = "geofeed.csv"
};
return await feedReturn.GetGeoFeed();
}
}
}

View File

@@ -4,6 +4,7 @@ using AS1024.GeoFeed.Core.GeoFeedProviders;
using Microsoft.EntityFrameworkCore;
using AS1024.GeoFeed.Core.GeoFeedSqliteLocalCache;
using AS1024.GeoFeed.Core.CacheService;
using AS1024.GeoFeed.Core.GeoFeedLogic;
namespace AS1024.GeoFeed
{
@@ -15,6 +16,7 @@ namespace AS1024.GeoFeed
builder.Services.AddHostedService<PreLoadGeoFeed>();
builder.Services.AddTransient<IGeoFeedProvider, NetBoxGeoFeedProvider>();
builder.Services.AddScoped<GeoFeedReturn>();
builder.Services.AddDbContext<GeoFeedCacheDbContext>(
options =>
{