From dff45c137ded9bc51330c977a9a1876888d3e66e Mon Sep 17 00:00:00 2001 From: Hinton Date: Thu, 13 Nov 2025 13:14:43 +0100 Subject: [PATCH] Add documentation --- .../QueryControllerTest.cs | 2 +- .../SeedControllerTest.cs | 2 +- util/Seeder/SceneResult.cs | 30 +++------ util/Seeder/Seeder.csproj | 4 -- util/SeederApi/Controllers/QueryController.cs | 11 +--- util/SeederApi/Controllers/SeedController.cs | 37 +++++------ .../Extensions/ServiceCollectionExtensions.cs | 4 +- .../Models/Request/QueryRequestModel.cs | 2 +- .../Models/Request/SeedRequestModel.cs | 2 +- util/SeederApi/Program.cs | 3 - util/SeederApi/Services/IQueryService.cs | 7 +++ util/SeederApi/Services/ISceneService.cs | 13 ++++ util/SeederApi/Services/SceneService.cs | 61 ------------------- 13 files changed, 52 insertions(+), 126 deletions(-) diff --git a/test/SeederApi.IntegrationTest/QueryControllerTest.cs b/test/SeederApi.IntegrationTest/QueryControllerTest.cs index 58381cec67..a453b2febb 100644 --- a/test/SeederApi.IntegrationTest/QueryControllerTest.cs +++ b/test/SeederApi.IntegrationTest/QueryControllerTest.cs @@ -1,5 +1,5 @@ using System.Net; -using Bit.SeederApi.Models.Requests; +using Bit.SeederApi.Models.Request; using Xunit; namespace Bit.SeederApi.IntegrationTest; diff --git a/test/SeederApi.IntegrationTest/SeedControllerTest.cs b/test/SeederApi.IntegrationTest/SeedControllerTest.cs index f41c851dd7..ced27d9f55 100644 --- a/test/SeederApi.IntegrationTest/SeedControllerTest.cs +++ b/test/SeederApi.IntegrationTest/SeedControllerTest.cs @@ -1,5 +1,5 @@ using System.Net; -using Bit.SeederApi.Models.Requests; +using Bit.SeederApi.Models.Request; using Bit.SeederApi.Models.Response; using Xunit; diff --git a/util/Seeder/SceneResult.cs b/util/Seeder/SceneResult.cs index 0e7abdce47..5c543c9004 100644 --- a/util/Seeder/SceneResult.cs +++ b/util/Seeder/SceneResult.cs @@ -1,33 +1,19 @@ namespace Bit.Seeder; -public class SceneResult : SceneResult -{ - public SceneResult(Dictionary mangleMap) - : base(result: null, mangleMap: mangleMap) { } -} +public class SceneResult(Dictionary mangleMap) + : SceneResult(result: null, mangleMap: mangleMap); -public class SceneResult +public class SceneResult(TResult result, Dictionary mangleMap) { - public TResult Result { get; init; } - public Dictionary MangleMap { get; init; } - - public SceneResult(TResult result, Dictionary mangleMap) - { - Result = result; - MangleMap = mangleMap; - } + public TResult Result { get; init; } = result; + public Dictionary MangleMap { get; init; } = mangleMap; public static explicit operator SceneResult(SceneResult v) { var result = v.Result; - if (result is null) - { - return new SceneResult(result: null, mangleMap: v.MangleMap); - } - else - { - return new SceneResult(result: result, mangleMap: v.MangleMap); - } + return result is null + ? new SceneResult(result: null, mangleMap: v.MangleMap) + : new SceneResult(result: result, mangleMap: v.MangleMap); } } diff --git a/util/Seeder/Seeder.csproj b/util/Seeder/Seeder.csproj index 4d7fbab767..fd6e26c1ee 100644 --- a/util/Seeder/Seeder.csproj +++ b/util/Seeder/Seeder.csproj @@ -12,10 +12,6 @@ false - - - - diff --git a/util/SeederApi/Controllers/QueryController.cs b/util/SeederApi/Controllers/QueryController.cs index d6a0dc1ccd..45eeccfaed 100644 --- a/util/SeederApi/Controllers/QueryController.cs +++ b/util/SeederApi/Controllers/QueryController.cs @@ -1,12 +1,11 @@ -using Bit.SeederApi.Models.Requests; +using Bit.SeederApi.Models.Request; using Bit.SeederApi.Services; using Microsoft.AspNetCore.Mvc; namespace Bit.SeederApi.Controllers; [Route("query")] -public class QueryController(ILogger logger, IQueryService queryService) - : Controller +public class QueryController(ILogger logger, IQueryService queryService) : Controller { [HttpPost] public IActionResult Query([FromBody] QueryRequestModel request) @@ -26,11 +25,7 @@ public class QueryController(ILogger logger, IQueryService quer catch (SceneExecutionException ex) { logger.LogError(ex, "Error executing query: {Query}", request.Template); - return BadRequest(new - { - Error = ex.Message, - Details = ex.InnerException?.Message - }); + return BadRequest(new { Error = ex.Message, Details = ex.InnerException?.Message }); } } } diff --git a/util/SeederApi/Controllers/SeedController.cs b/util/SeederApi/Controllers/SeedController.cs index c3f22ec91d..58854fbda7 100644 --- a/util/SeederApi/Controllers/SeedController.cs +++ b/util/SeederApi/Controllers/SeedController.cs @@ -1,4 +1,4 @@ -using Bit.SeederApi.Models.Requests; +using Bit.SeederApi.Models.Request; using Bit.SeederApi.Models.Response; using Bit.SeederApi.Services; using Microsoft.AspNetCore.Mvc; @@ -6,18 +6,20 @@ using Microsoft.AspNetCore.Mvc; namespace Bit.SeederApi.Controllers; [Route("seed")] -public class SeedController(ILogger logger, ISceneService sceneService, IServiceProvider serviceProvider) - : Controller +public class SeedController( + ILogger logger, + ISceneService sceneService, + IServiceProvider serviceProvider) : Controller { [HttpPost] - public async Task Seed([FromBody] SeedRequestModel request) + public async Task SeedAsync([FromBody] SeedRequestModel request) { logger.LogInformation("Received seed request {Provider}", serviceProvider.GetType().FullName); logger.LogInformation("Seeding with template: {Template}", request.Template); try { - SceneResponseModel response = await sceneService.ExecuteScene(request.Template, request.Arguments); + var response = await sceneService.ExecuteScene(request.Template, request.Arguments); return Json(response); } @@ -28,16 +30,12 @@ public class SeedController(ILogger logger, ISceneService sceneS catch (SceneExecutionException ex) { logger.LogError(ex, "Error executing scene: {Template}", request.Template); - return BadRequest(new - { - Error = ex.Message, - Details = ex.InnerException?.Message - }); + return BadRequest(new { Error = ex.Message, Details = ex.InnerException?.Message }); } } [HttpDelete("batch")] - public async Task DeleteBatch([FromBody] List playIds) + public async Task DeleteBatchAsync([FromBody] List playIds) { logger.LogInformation("Deleting batch of seeded data with IDs: {PlayIds}", string.Join(", ", playIds)); @@ -67,14 +65,12 @@ public class SeedController(ILogger logger, ISceneService sceneS Details = aggregateException.InnerExceptions.Select(e => e.Message).ToList() }); } - return Ok(new - { - Message = "Batch delete completed successfully" - }); + + return Ok(new { Message = "Batch delete completed successfully" }); } [HttpDelete("{playId}")] - public async Task Delete([FromRoute] string playId) + public async Task DeleteAsync([FromRoute] string playId) { logger.LogInformation("Deleting seeded data with ID: {PlayId}", playId); @@ -87,17 +83,13 @@ public class SeedController(ILogger logger, ISceneService sceneS catch (SceneExecutionException ex) { logger.LogError(ex, "Error deleting seeded data: {PlayId}", playId); - return BadRequest(new - { - Error = ex.Message, - Details = ex.InnerException?.Message - }); + return BadRequest(new { Error = ex.Message, Details = ex.InnerException?.Message }); } } [HttpDelete] - public async Task DeleteAll() + public async Task DeleteAllAsync() { logger.LogInformation("Deleting all seeded data"); @@ -128,6 +120,7 @@ public class SeedController(ILogger logger, ISceneService sceneS Details = aggregateException.InnerExceptions.Select(e => e.Message).ToList() }); } + return NoContent(); } } diff --git a/util/SeederApi/Extensions/ServiceCollectionExtensions.cs b/util/SeederApi/Extensions/ServiceCollectionExtensions.cs index 2a22b199d0..eae01abdf6 100644 --- a/util/SeederApi/Extensions/ServiceCollectionExtensions.cs +++ b/util/SeederApi/Extensions/ServiceCollectionExtensions.cs @@ -7,7 +7,7 @@ namespace Bit.SeederApi.Extensions; public static class ServiceCollectionExtensions { /// - /// Dynamically registers all scene types that implement IScene from the Seeder assembly. + /// Dynamically registers all scene types that implement IScene<TRequest> from the Seeder assembly. /// Scenes are registered as keyed scoped services using their class name as the key. /// public static IServiceCollection AddScenes(this IServiceCollection services) @@ -32,7 +32,7 @@ public static class ServiceCollectionExtensions } /// - /// Dynamically registers all query types that implement IQuery from the Seeder assembly. + /// Dynamically registers all query types that implement IQuery<TRequest> from the Seeder assembly. /// Queries are registered as keyed scoped services using their class name as the key. /// public static IServiceCollection AddQueries(this IServiceCollection services) diff --git a/util/SeederApi/Models/Request/QueryRequestModel.cs b/util/SeederApi/Models/Request/QueryRequestModel.cs index cf037205c5..38751bc21b 100644 --- a/util/SeederApi/Models/Request/QueryRequestModel.cs +++ b/util/SeederApi/Models/Request/QueryRequestModel.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json; -namespace Bit.SeederApi.Models.Requests; +namespace Bit.SeederApi.Models.Request; public class QueryRequestModel { diff --git a/util/SeederApi/Models/Request/SeedRequestModel.cs b/util/SeederApi/Models/Request/SeedRequestModel.cs index 91c88d1df4..404af97ebe 100644 --- a/util/SeederApi/Models/Request/SeedRequestModel.cs +++ b/util/SeederApi/Models/Request/SeedRequestModel.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json; -namespace Bit.SeederApi.Models.Requests; +namespace Bit.SeederApi.Models.Request; public class SeedRequestModel { diff --git a/util/SeederApi/Program.cs b/util/SeederApi/Program.cs index b0b4a856b6..9e8574656f 100644 --- a/util/SeederApi/Program.cs +++ b/util/SeederApi/Program.cs @@ -41,6 +41,3 @@ app.UseRouting(); app.MapControllerRoute(name: "default", pattern: "{controller=Seed}/{action=Index}/{id?}"); app.Run(); - -// Make Program class accessible for integration tests -public partial class Program { } diff --git a/util/SeederApi/Services/IQueryService.cs b/util/SeederApi/Services/IQueryService.cs index 6ac41da960..e13f7fff63 100644 --- a/util/SeederApi/Services/IQueryService.cs +++ b/util/SeederApi/Services/IQueryService.cs @@ -2,6 +2,13 @@ namespace Bit.SeederApi.Services; +/// +/// Service for executing query operations. +/// +/// +/// The query service provides a mechanism to execute read-only query operations by name with optional JSON arguments. +/// Queries retrieve existing data from the system without modifying state or tracking entities. +/// public interface IQueryService { /// diff --git a/util/SeederApi/Services/ISceneService.cs b/util/SeederApi/Services/ISceneService.cs index 20a279c4ba..128316692f 100644 --- a/util/SeederApi/Services/ISceneService.cs +++ b/util/SeederApi/Services/ISceneService.cs @@ -3,6 +3,14 @@ using Bit.SeederApi.Models.Response; namespace Bit.SeederApi.Services; +/// +/// Service for executing and managing scene operations. +/// +/// +/// The scene service provides a mechanism to execute scene operations by name with optional JSON arguments. +/// Scenes create and configure test data, track entities for cleanup, and support destruction of seeded data. +/// Each scene execution can be assigned a play ID for tracking and subsequent cleanup operations. +/// public interface ISceneService { /// @@ -22,5 +30,10 @@ public interface ISceneService /// The result of the destroy operation /// Thrown when there's an error destroying the seeded data Task DestroyScene(string playId); + + /// + /// Retrieves all play IDs for currently tracked seeded data. + /// + /// A list of play IDs representing active seeded data that can be destroyed. List GetAllPlayIds(); } diff --git a/util/SeederApi/Services/SceneService.cs b/util/SeederApi/Services/SceneService.cs index 6aaddfecdc..5cfda54a0e 100644 --- a/util/SeederApi/Services/SceneService.cs +++ b/util/SeederApi/Services/SceneService.cs @@ -35,67 +35,6 @@ public class SceneService( return SceneResponseModel.FromSceneResult(result); } - public object ExecuteQuery(string queryName, JsonElement? arguments) - { - try - { - var query = serviceProvider.GetKeyedService(queryName) - ?? throw new SceneNotFoundException(queryName); - - var requestType = query.GetRequestType(); - - // Deserialize the arguments into the request model - object? requestModel; - if (arguments == null) - { - // Try to create an instance with default values - try - { - requestModel = Activator.CreateInstance(requestType); - if (requestModel == null) - { - throw new SceneExecutionException( - $"Arguments are required for query '{queryName}'"); - } - } - catch - { - throw new SceneExecutionException( - $"Arguments are required for query '{queryName}'"); - } - } - else - { - try - { - requestModel = JsonSerializer.Deserialize(arguments.Value.GetRawText(), requestType, _jsonOptions); - if (requestModel == null) - { - throw new SceneExecutionException( - $"Failed to deserialize request model for query '{queryName}'"); - } - } - catch (JsonException ex) - { - throw new SceneExecutionException( - $"Failed to deserialize request model for query '{queryName}': {ex.Message}", ex); - } - } - - var result = query.Execute(requestModel); - - logger.LogInformation("Successfully executed query: {QueryName}", queryName); - return result; - } - catch (Exception ex) when (ex is not SceneNotFoundException and not SceneExecutionException) - { - logger.LogError(ex, "Unexpected error executing query: {QueryName}", queryName); - throw new SceneExecutionException( - $"An unexpected error occurred while executing query '{queryName}'", - ex.InnerException ?? ex); - } - } - public async Task DestroyScene(string playId) { // Note, delete cascade will remove PlayData entries