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)