1
0
mirror of https://github.com/bitwarden/server synced 2026-01-15 15:03:34 +00:00
Files
server/util/Seeder/IScene.cs
Oscar Hinton f144828a87 [PM-22263] [PM-29849] Initial PoC of seeder API (#6424)
We want to reduce the amount of business critical test data in the company. One way of doing that is to generate test data on demand prior to client side testing.

Clients will request a scene to be set up with a JSON body set of options, specific to a given scene. Successful seed requests will be responded to with a mangleMap which maps magic strings present in the request to the mangled, non-colliding versions inserted into the database. This way, the server is solely responsible for understanding uniqueness requirements in the database. scenes also are able to return custom data, depending on the scene. For example, user creation would benefit from a return value of the userId for further test setup on the client side.

Clients will indicate they are running tests by including a unique header, x-play-id which specifies a unique testing context. The server uses this PlayId as the seed for any mangling that occurs. This allows the client to decide it will reuse a given PlayId if the test context builds on top of previously executed tests. When a given context is no longer needed, the API user will delete all test data associated with the PlayId by calling a delete endpoint.

---------

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
2026-01-13 11:10:01 -06:00

97 lines
4.5 KiB
C#

namespace Bit.Seeder;
/// <summary>
/// Base interface for seeding operations. The base interface should not be used directly, rather use `IScene&lt;Request&gt;`.
/// </summary>
/// <remarks>
/// Scenes are components in the seeding system that create and configure test data. They follow
/// a type-safe pattern using generics to ensure proper request/response handling while maintaining
/// a common non-generic interface for dynamic invocation.
/// </remarks>
public interface IScene
{
/// <summary>
/// Gets the type of request this scene expects.
/// </summary>
/// <returns>The request type that this scene can process.</returns>
Type GetRequestType();
/// <summary>
/// Seeds data based on the provided request object.
/// </summary>
/// <param name="request">The request object containing parameters for the seeding operation.</param>
/// <returns>A scene result containing any returned data, mangle map, and entity tracking information.</returns>
Task<SceneResult<object?>> SeedAsync(object request);
}
/// <summary>
/// Generic scene interface for seeding operations with a specific request type. Does not return a value beyond tracking entities and a mangle map.
/// </summary>
/// <typeparam name="TRequest">The type of request object this scene accepts.</typeparam>
/// <remarks>
/// Use this interface when your scene needs to process a specific request type but doesn't need to
/// return any data beyond the standard mangle map for ID transformations and entity tracking.
/// The explicit interface implementations allow this scene to be invoked dynamically through the
/// base IScene interface while maintaining type safety in the implementation.
/// </remarks>
public interface IScene<TRequest> : IScene where TRequest : class
{
/// <summary>
/// Seeds data based on the provided strongly-typed request.
/// </summary>
/// <param name="request">The request object containing parameters for the seeding operation.</param>
/// <returns>A scene result containing the mangle map and entity tracking information.</returns>
Task<SceneResult> SeedAsync(TRequest request);
/// <summary>
/// Gets the request type for this scene.
/// </summary>
/// <returns>The type of TRequest.</returns>
Type IScene.GetRequestType() => typeof(TRequest);
/// <summary>
/// Adapts the non-generic SeedAsync to the strongly-typed version.
/// </summary>
/// <param name="request">The request object to cast and process.</param>
/// <returns>A scene result wrapped as an object result.</returns>
async Task<SceneResult<object?>> IScene.SeedAsync(object request)
{
var result = await SeedAsync((TRequest)request);
return new SceneResult(mangleMap: result.MangleMap);
}
}
/// <summary>
/// Generic scene interface for seeding operations with a specific request type that returns typed data.
/// </summary>
/// <typeparam name="TRequest">The type of request object this scene accepts. Must be a reference type.</typeparam>
/// <typeparam name="TResult">The type of data this scene returns. Must be a reference type.</typeparam>
/// <remarks>
/// Use this interface when your scene needs to return specific data that can be used by subsequent
/// scenes or test logic. The result is wrapped in a SceneResult that also includes the mangle map
/// and entity tracking information. The explicit interface implementations allow dynamic invocation
/// while preserving type safety in the implementation.
/// </remarks>
public interface IScene<TRequest, TResult> : IScene where TRequest : class where TResult : class
{
/// <summary>
/// Seeds data based on the provided strongly-typed request and returns typed result data.
/// </summary>
/// <param name="request">The request object containing parameters for the seeding operation.</param>
/// <returns>A scene result containing the typed result data, mangle map, and entity tracking information.</returns>
Task<SceneResult<TResult>> SeedAsync(TRequest request);
/// <summary>
/// Gets the request type for this scene.
/// </summary>
/// <returns>The type of TRequest.</returns>
Type IScene.GetRequestType() => typeof(TRequest);
/// <summary>
/// Adapts the non-generic SeedAsync to the strongly-typed version.
/// </summary>
/// <param name="request">The request object to cast and process.</param>
/// <returns>A scene result with the typed result cast to object.</returns>
async Task<SceneResult<object?>> IScene.SeedAsync(object request) => (SceneResult<object?>)await SeedAsync((TRequest)request);
}