diff --git a/util/Seeder/Recipes/OrganizationWithUsersRecipe.cs b/util/Seeder/Recipes/OrganizationWithUsersRecipe.cs index fb06c091ae..5443e3d70e 100644 --- a/util/Seeder/Recipes/OrganizationWithUsersRecipe.cs +++ b/util/Seeder/Recipes/OrganizationWithUsersRecipe.cs @@ -1,7 +1,9 @@ -using Bit.Infrastructure.EntityFramework.Models; +using Bit.Infrastructure.EntityFramework.AdminConsole.Models; +using Bit.Infrastructure.EntityFramework.Models; using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Seeder.Factories; using LinqToDB.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; namespace Bit.Seeder.Recipes; @@ -34,4 +36,18 @@ public class OrganizationWithUsersRecipe(DatabaseContext db) return organization.Id; } + + public void Destroy(Guid organizationId) + { + var organization = db.Organizations.Include(o => o.OrganizationUsers) + .ThenInclude(ou => ou.User).FirstOrDefault(p => p.Id == organizationId); + if (organization == null) + { + throw new Exception($"Organization with ID {organizationId} not found."); + } + var users = organization.OrganizationUsers.Select(u => u.User); + + db.RemoveRange(users); + db.Remove(organization); + } } diff --git a/util/SeederApi/Controllers/SeedController.cs b/util/SeederApi/Controllers/SeedController.cs index 9c4282f07c..17a91f50cd 100644 --- a/util/SeederApi/Controllers/SeedController.cs +++ b/util/SeederApi/Controllers/SeedController.cs @@ -64,9 +64,43 @@ public class SeedController : Controller } } - [HttpGet("/delete")] - public string Delete() + [HttpDelete("/delete")] + public IActionResult Delete([FromBody] SeedRequestModel request) { - return "hello delete"; + _logger.LogInformation("Deleting with template: {Template}", request.Template); + + try + { + var result = _recipeService.DestroyRecipe(request.Template, request.Arguments); + + return Ok(new + { + Message = "Delete completed successfully", + request.Template, + Result = result + }); + } + catch (RecipeNotFoundException ex) + { + return NotFound(new { Error = ex.Message }); + } + catch (RecipeExecutionException ex) + { + _logger.LogError(ex, "Error executing recipe delete: {Template}", request.Template); + return BadRequest(new + { + Error = ex.Message, + Details = ex.InnerException?.Message + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Unexpected error deleting with template: {Template}", request.Template); + return StatusCode(500, new + { + Error = "An unexpected error occurred while deleting", + Details = ex.Message + }); + } } } diff --git a/util/SeederApi/Services/IRecipeService.cs b/util/SeederApi/Services/IRecipeService.cs index 61e8bd1da9..b935eca82c 100644 --- a/util/SeederApi/Services/IRecipeService.cs +++ b/util/SeederApi/Services/IRecipeService.cs @@ -13,4 +13,14 @@ public interface IRecipeService /// Thrown when the recipe template is not found /// Thrown when there's an error executing the recipe object? ExecuteRecipe(string templateName, JsonElement? arguments); + + /// + /// Destroys data created by a recipe with the given template name and arguments. + /// + /// The name of the recipe template (e.g., "OrganizationWithUsersRecipe") + /// Optional JSON arguments to pass to the recipe's Destroy method + /// The result returned by the recipe's Destroy method + /// Thrown when the recipe template is not found + /// Thrown when there's an error executing the recipe + object? DestroyRecipe(string templateName, JsonElement? arguments); } diff --git a/util/SeederApi/Services/RecipeService.cs b/util/SeederApi/Services/RecipeService.cs index b449d5ba90..77ab196639 100644 --- a/util/SeederApi/Services/RecipeService.cs +++ b/util/SeederApi/Services/RecipeService.cs @@ -16,24 +16,34 @@ public class RecipeService : IRecipeService } public object? ExecuteRecipe(string templateName, JsonElement? arguments) + { + return ExecuteRecipeMethod(templateName, arguments, "Seed"); + } + + public object? DestroyRecipe(string templateName, JsonElement? arguments) + { + return ExecuteRecipeMethod(templateName, arguments, "Destroy"); + } + + private object? ExecuteRecipeMethod(string templateName, JsonElement? arguments, string methodName) { try { var recipeType = LoadRecipeType(templateName); - var seedMethod = GetSeedMethod(recipeType, templateName); + var method = GetRecipeMethod(recipeType, templateName, methodName); var recipeInstance = Activator.CreateInstance(recipeType, _databaseContext)!; - var methodArguments = ParseMethodArguments(seedMethod, arguments); - var result = seedMethod.Invoke(recipeInstance, methodArguments); + var methodArguments = ParseMethodArguments(method, arguments); + var result = method.Invoke(recipeInstance, methodArguments); - _logger.LogInformation("Successfully executed recipe: {TemplateName}", templateName); + _logger.LogInformation("Successfully executed {MethodName} on recipe: {TemplateName}", methodName, templateName); return result; } catch (Exception ex) when (ex is not RecipeNotFoundException and not RecipeExecutionException) { - _logger.LogError(ex, "Unexpected error executing recipe: {TemplateName}", templateName); + _logger.LogError(ex, "Unexpected error executing {MethodName} on recipe: {TemplateName}", methodName, templateName); throw new RecipeExecutionException( - $"An unexpected error occurred while executing recipe '{templateName}'", + $"An unexpected error occurred while executing {methodName} on recipe '{templateName}'", ex.InnerException ?? ex); } } @@ -48,10 +58,10 @@ public class RecipeService : IRecipeService return recipeType ?? throw new RecipeNotFoundException(templateName); } - private static MethodInfo GetSeedMethod(Type recipeType, string templateName) + private static MethodInfo GetRecipeMethod(Type recipeType, string templateName, string methodName) { - var seedMethod = recipeType.GetMethod("Seed"); - return seedMethod ?? throw new RecipeExecutionException($"Seed method not found in recipe '{templateName}'"); + var method = recipeType.GetMethod(methodName); + return method ?? throw new RecipeExecutionException($"{methodName} method not found in recipe '{templateName}'"); } private static object?[] ParseMethodArguments(MethodInfo seedMethod, JsonElement? arguments)