Skip to main content

fraiseql_server/tenancy/
mod.rs

1//! Multi-tenancy infrastructure: pool factory, executor construction, health monitoring.
2
3pub mod audit;
4pub mod pool_factory;
5pub mod schema_isolation;
6
7#[cfg(test)]
8mod tests;
9
10use std::{future::Future, pin::Pin, sync::Arc};
11
12use fraiseql_core::runtime::Executor;
13use fraiseql_error::Result;
14pub use pool_factory::{
15    FromPoolConfig, TenantPoolConfig, create_tenant_executor, destroy_tenant_schema,
16};
17
18/// Type-erased async factory for creating tenant executors.
19///
20/// Stored in `AppState` so that the management API handler (`upsert_tenant_handler`)
21/// can build an `Executor<A>` without requiring `A: FromPoolConfig` as a bound on
22/// the route handler or the `Server<A>` impl. The factory is set once at server
23/// startup by code that knows the concrete adapter type.
24pub type TenantExecutorFactory<A> = Arc<
25    dyn Fn(
26            String,
27            String,
28            TenantPoolConfig,
29        ) -> Pin<Box<dyn Future<Output = Result<Arc<Executor<A>>>> + Send>>
30        + Send
31        + Sync,
32>;
33
34/// Create a `TenantExecutorFactory` for an adapter that implements `FromPoolConfig`.
35///
36/// Captures the `FromPoolConfig` bound at construction time so that the factory
37/// can be stored as a type-erased closure in `AppState`.
38///
39/// The first argument is the tenant key, used for schema isolation naming.
40#[must_use]
41pub fn make_executor_factory<A: FromPoolConfig + 'static>() -> TenantExecutorFactory<A> {
42    Arc::new(|tenant_key, schema_json, pool_config| {
43        Box::pin(async move {
44            create_tenant_executor::<A>(&tenant_key, &schema_json, &pool_config).await
45        })
46    })
47}