1
0
mirror of https://github.com/bitwarden/server synced 2025-12-15 15:53:59 +00:00

Add support for destroying seeded data

This commit is contained in:
Hinton
2025-10-07 14:59:17 -07:00
parent 2b340b72da
commit 79f5d8f147
4 changed files with 83 additions and 13 deletions

View File

@@ -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.Infrastructure.EntityFramework.Repositories;
using Bit.Seeder.Factories; using Bit.Seeder.Factories;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Bit.Seeder.Recipes; namespace Bit.Seeder.Recipes;
@@ -34,4 +36,18 @@ public class OrganizationWithUsersRecipe(DatabaseContext db)
return organization.Id; 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);
}
} }

View File

@@ -64,9 +64,43 @@ public class SeedController : Controller
} }
} }
[HttpGet("/delete")] [HttpDelete("/delete")]
public string 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
});
}
} }
} }

View File

@@ -13,4 +13,14 @@ public interface IRecipeService
/// <exception cref="RecipeNotFoundException">Thrown when the recipe template is not found</exception> /// <exception cref="RecipeNotFoundException">Thrown when the recipe template is not found</exception>
/// <exception cref="RecipeExecutionException">Thrown when there's an error executing the recipe</exception> /// <exception cref="RecipeExecutionException">Thrown when there's an error executing the recipe</exception>
object? ExecuteRecipe(string templateName, JsonElement? arguments); object? ExecuteRecipe(string templateName, JsonElement? arguments);
/// <summary>
/// Destroys data created by a recipe with the given template name and arguments.
/// </summary>
/// <param name="templateName">The name of the recipe template (e.g., "OrganizationWithUsersRecipe")</param>
/// <param name="arguments">Optional JSON arguments to pass to the recipe's Destroy method</param>
/// <returns>The result returned by the recipe's Destroy method</returns>
/// <exception cref="RecipeNotFoundException">Thrown when the recipe template is not found</exception>
/// <exception cref="RecipeExecutionException">Thrown when there's an error executing the recipe</exception>
object? DestroyRecipe(string templateName, JsonElement? arguments);
} }

View File

@@ -16,24 +16,34 @@ public class RecipeService : IRecipeService
} }
public object? ExecuteRecipe(string templateName, JsonElement? arguments) 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 try
{ {
var recipeType = LoadRecipeType(templateName); var recipeType = LoadRecipeType(templateName);
var seedMethod = GetSeedMethod(recipeType, templateName); var method = GetRecipeMethod(recipeType, templateName, methodName);
var recipeInstance = Activator.CreateInstance(recipeType, _databaseContext)!; var recipeInstance = Activator.CreateInstance(recipeType, _databaseContext)!;
var methodArguments = ParseMethodArguments(seedMethod, arguments); var methodArguments = ParseMethodArguments(method, arguments);
var result = seedMethod.Invoke(recipeInstance, methodArguments); 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; return result;
} }
catch (Exception ex) when (ex is not RecipeNotFoundException and not RecipeExecutionException) 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( throw new RecipeExecutionException(
$"An unexpected error occurred while executing recipe '{templateName}'", $"An unexpected error occurred while executing {methodName} on recipe '{templateName}'",
ex.InnerException ?? ex); ex.InnerException ?? ex);
} }
} }
@@ -48,10 +58,10 @@ public class RecipeService : IRecipeService
return recipeType ?? throw new RecipeNotFoundException(templateName); 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"); var method = recipeType.GetMethod(methodName);
return seedMethod ?? throw new RecipeExecutionException($"Seed method not found in recipe '{templateName}'"); return method ?? throw new RecipeExecutionException($"{methodName} method not found in recipe '{templateName}'");
} }
private static object?[] ParseMethodArguments(MethodInfo seedMethod, JsonElement? arguments) private static object?[] ParseMethodArguments(MethodInfo seedMethod, JsonElement? arguments)