From b7230950b26f523f751685faeb85250d9fe21b95 Mon Sep 17 00:00:00 2001 From: Kara Alexandra Date: Wed, 26 Feb 2025 15:58:45 -0600 Subject: [PATCH] Add seed-fetching service --- ALttPRandomizer/Azure/AzureStorage.cs | 57 +++++++++++++++++++ ALttPRandomizer/GenerateSeedController.cs | 15 ++++- ALttPRandomizer/IdGenerator.cs | 2 +- ALttPRandomizer/Program.cs | 12 ++-- .../Properties/launchSettings.json | 9 +++ ALttPRandomizer/Randomizer.cs | 30 ++++------ ALttPRandomizer/Service/SeedService.cs | 25 ++++++++ 7 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 ALttPRandomizer/Azure/AzureStorage.cs create mode 100644 ALttPRandomizer/Properties/launchSettings.json create mode 100644 ALttPRandomizer/Service/SeedService.cs diff --git a/ALttPRandomizer/Azure/AzureStorage.cs b/ALttPRandomizer/Azure/AzureStorage.cs new file mode 100644 index 0000000..6cea835 --- /dev/null +++ b/ALttPRandomizer/Azure/AzureStorage.cs @@ -0,0 +1,57 @@ +namespace ALttPRandomizer.Azure { + using global::Azure.Storage.Blobs; + using Microsoft.Extensions.Logging; + using System; + using System.Collections.Generic; + using System.IO; + using System.Threading.Tasks; + + public class AzureStorage { + public AzureStorage(BlobContainerClient blobClient, ILogger logger) { + this.BlobClient = blobClient; + this.Logger = logger; + } + + private ILogger Logger { get; } + private BlobContainerClient BlobClient { get; } + + public async Task UploadFile(string name, Stream data) { + await BlobClient.UploadBlobAsync(name, data); + } + + public async Task UploadFileAndDelete(string name, string filepath) { + using (var stream = new FileStream(filepath, FileMode.Open, FileAccess.Read)) { + this.Logger.LogDebug("Uploading file {filepath} -> {name}", filepath, name); + await this.UploadFile(name, stream); + } + + this.Logger.LogDebug("Deleting file {filepath}", filepath); + File.Delete(filepath); + } + + public async Task> GetFiles(string seedId) { + var prefix = seedId + "/"; + var blobs = this.BlobClient.GetBlobsAsync(prefix: prefix); + + var data = new Dictionary(); + + await foreach (var blob in blobs) { + var result = await this.BlobClient.GetBlobClient(blob.Name).DownloadContentAsync(); + if (result.Value.Details.ContentLength == 0) { + continue; + } + + if (!blob.Name.StartsWith(prefix)) { + this.Logger.LogWarning("Found prefix mismatch for seed id {seedId}, blob name {blobName}", seedId, blob.Name); + continue; + } + + var suffix = blob.Name.Substring(prefix.Length); + + data[suffix] = result.Value.Content; + } + + return data; + } + } +} diff --git a/ALttPRandomizer/GenerateSeedController.cs b/ALttPRandomizer/GenerateSeedController.cs index d10a519..7378ecb 100644 --- a/ALttPRandomizer/GenerateSeedController.cs +++ b/ALttPRandomizer/GenerateSeedController.cs @@ -1,19 +1,30 @@ namespace ALttPRandomizer { using ALttPRandomizer.Model; + using ALttPRandomizer.Service; using Microsoft.AspNetCore.Mvc; + using System.Threading.Tasks; public class GenerateController : Controller { - public GenerateController(Randomizer randomizer) { + public GenerateController(Randomizer randomizer, SeedService seedService) { this.Randomizer = randomizer; + this.SeedService = seedService; } private Randomizer Randomizer { get; } + private SeedService SeedService { get; } [Route("/generate")] [HttpPost] public ActionResult Generate(SeedSettings settings) { var result = this.Randomizer.Randomize(); - return Content(result); + return Ok(result); + } + + [Route("/seed/{id}")] + [HttpGet] + public async Task GetSeed(string id) { + var result = await this.SeedService.GetSeed(id); + return Ok(result); } } } diff --git a/ALttPRandomizer/IdGenerator.cs b/ALttPRandomizer/IdGenerator.cs index 17f8d00..33b2696 100644 --- a/ALttPRandomizer/IdGenerator.cs +++ b/ALttPRandomizer/IdGenerator.cs @@ -4,7 +4,7 @@ public class IdGenerator { private const string chars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; private const int length = 10; - private static Random random = new Random(); + private static readonly Random random = new Random(); public string GenerateId() { var str = new char[length]; diff --git a/ALttPRandomizer/Program.cs b/ALttPRandomizer/Program.cs index fb702be..4f78c7a 100644 --- a/ALttPRandomizer/Program.cs +++ b/ALttPRandomizer/Program.cs @@ -1,9 +1,11 @@ namespace ALttPRandomizer { using System.Text.Json.Serialization; + using ALttPRandomizer.Azure; using ALttPRandomizer.Options; - using Azure.Identity; - using Azure.Storage.Blobs; + using ALttPRandomizer.Service; + using global::Azure.Identity; + using global::Azure.Storage.Blobs; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -36,8 +38,10 @@ var seedClient = new BlobContainerClient(settings.AzureSettings.BlobstoreEndpoint, token); builder.Services.AddSingleton(seedClient); - builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddSingleton(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); var app = builder.Build(); diff --git a/ALttPRandomizer/Properties/launchSettings.json b/ALttPRandomizer/Properties/launchSettings.json new file mode 100644 index 0000000..a1ef8bc --- /dev/null +++ b/ALttPRandomizer/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "https": { + "applicationUrl": "localhost:5000/swagger", + "commandName": "Project", + "launchBrowser": true + } + } +} \ No newline at end of file diff --git a/ALttPRandomizer/Randomizer.cs b/ALttPRandomizer/Randomizer.cs index 2ab5b30..c897971 100644 --- a/ALttPRandomizer/Randomizer.cs +++ b/ALttPRandomizer/Randomizer.cs @@ -1,26 +1,25 @@ namespace ALttPRandomizer { + using ALttPRandomizer.Azure; using ALttPRandomizer.Options; - using Azure.Storage.Blobs; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; - using System; using System.Diagnostics; using System.IO; using System.Threading.Tasks; public class Randomizer { public Randomizer( - IdGenerator idGenerator, - BlobContainerClient seedClient, - IOptionsMonitor optionsMonitor, - ILogger logger) { + IdGenerator idGenerator, + AzureStorage azureStorage, + IOptionsMonitor optionsMonitor, + ILogger logger) { this.IdGenerator = idGenerator; - this.SeedClient = seedClient; + this.AzureStorage = azureStorage; this.OptionsMonitor = optionsMonitor; this.Logger = logger; } - private BlobContainerClient SeedClient { get; } + private AzureStorage AzureStorage { get; } private IOptionsMonitor OptionsMonitor { get; } private IdGenerator IdGenerator { get; } private ILogger Logger { get; } @@ -81,26 +80,17 @@ var spoilerIn = Path.Join(Path.GetTempPath(), string.Format("DR_{0}_Spoiler.txt", id)); var spoilerOut = string.Format("{0}/spoiler.txt", id); - var uploadPatch = UploadFile(bpsOut, bpsIn); - var uploadSpoiler = UploadFile(spoilerOut, spoilerIn); + var uploadPatch = this.AzureStorage.UploadFileAndDelete(bpsOut, bpsIn); + var uploadSpoiler = this.AzureStorage.UploadFileAndDelete(spoilerOut, spoilerIn); await Task.WhenAll(uploadPatch, uploadSpoiler); + this.Logger.LogDebug("Deleting file {filepath}", rom); File.Delete(rom); this.Logger.LogDebug("Finished uploading seed id {id}", id); } - private async Task UploadFile(string name, string filepath) { - using (var stream = new FileStream(filepath, FileMode.Open, FileAccess.Read)) { - this.Logger.LogDebug("Uploading file {filepath} -> {name}", filepath, name); - await this.SeedClient.UploadBlobAsync(name, stream); - } - - this.Logger.LogDebug("Deleting file {filepath}", filepath); - File.Delete(filepath); - } - private void GenerationFailed(string id, int exitcode) { } } diff --git a/ALttPRandomizer/Service/SeedService.cs b/ALttPRandomizer/Service/SeedService.cs new file mode 100644 index 0000000..0effe96 --- /dev/null +++ b/ALttPRandomizer/Service/SeedService.cs @@ -0,0 +1,25 @@ +namespace ALttPRandomizer.Service { + using ALttPRandomizer.Azure; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + public class SeedService { + public SeedService(AzureStorage azureStorage) { + this.AzureStorage = azureStorage; + } + + private AzureStorage AzureStorage { get; } + + public async Task> GetSeed(string seedId) { + var files = await this.AzureStorage.GetFiles(seedId); + + var result = new Dictionary(); + foreach (var file in files) { + result[file.Key] = Convert.ToBase64String(file.Value.ToMemory().ToArray()); + } + + return result; + } + } +}