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:
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user