pub struct CrudService;Implementations§
Source§impl CrudService
impl CrudService
Sourcepub async fn list<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
filter: Option<&FilterNode>,
sort: &[SortSpec],
limit: Option<u32>,
offset: Option<u32>,
filter_includes: &[IncludeSelect<'_>],
schema_override: Option<&str>,
dialect: &dyn Dialect,
registry: Option<&ExtensibleRegistry>,
) -> Result<Vec<Value>, AppError>
pub async fn list<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, filter: Option<&FilterNode>, sort: &[SortSpec], limit: Option<u32>, offset: Option<u32>, filter_includes: &[IncludeSelect<'_>], schema_override: Option<&str>, dialect: &dyn Dialect, registry: Option<&ExtensibleRegistry>, ) -> Result<Vec<Value>, AppError>
List rows with optional RSQL filter and sort, limit (default 100, max 1000), offset (default 0).
filter_includes supplies related-entity metadata for dotted-field EXISTS filters; pass &[] when unused.
Sourcepub async fn list_with_includes<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
filter: Option<&FilterNode>,
sort: &[SortSpec],
limit: Option<u32>,
offset: Option<u32>,
includes: &[IncludeSelect<'_>],
filter_includes: &[IncludeSelect<'_>],
schema_override: Option<&str>,
dialect: &dyn Dialect,
registry: Option<&ExtensibleRegistry>,
) -> Result<Vec<Value>, AppError>
pub async fn list_with_includes<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, filter: Option<&FilterNode>, sort: &[SortSpec], limit: Option<u32>, offset: Option<u32>, includes: &[IncludeSelect<'_>], filter_includes: &[IncludeSelect<'_>], schema_override: Option<&str>, dialect: &dyn Dialect, registry: Option<&ExtensibleRegistry>, ) -> Result<Vec<Value>, AppError>
List rows with includes in a single query (scalar subqueries with json_agg/row_to_json). Returns rows with include keys already set (JSON).
includes drives scalar subqueries for response data; filter_includes is the superset used for EXISTS generation.
Sourcepub async fn read<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
id: &Value,
schema_override: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Option<Value>, AppError>
pub async fn read<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, id: &Value, schema_override: Option<&str>, dialect: &dyn Dialect, ) -> Result<Option<Value>, AppError>
Fetch one row by primary key. Returns JSON object or None.
Sourcepub async fn fetch_where_column_in<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
column_name: &str,
values: &[Value],
schema_override: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Vec<Value>, AppError>
pub async fn fetch_where_column_in<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, column_name: &str, values: &[Value], schema_override: Option<&str>, dialect: &dyn Dialect, ) -> Result<Vec<Value>, AppError>
Fetch rows from entity where column IN (values). Used for batch-loading related rows.
Sourcepub async fn create<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
body: &HashMap<String, Value>,
schema_override: Option<&str>,
rls_tenant_id: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Value, AppError>
pub async fn create<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, body: &HashMap<String, Value>, schema_override: Option<&str>, rls_tenant_id: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<Value, AppError>
Insert one row; body may include or omit PK (if has default). Returns created row. When rls_tenant_id is Some (RLS strategy), tenant_id column is set automatically. When caller_user_id is Some, created_by is set to that value.
Sourcepub async fn create_graph(
pool: &Pool,
parent: &ResolvedEntity,
parent_body: &HashMap<String, Value>,
children: &[GraphChild],
schema_override: Option<&str>,
rls_tenant_id: Option<&str>,
set_local_sql: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<(Value, HashMap<String, Vec<Value>>), AppError>
pub async fn create_graph( pool: &Pool, parent: &ResolvedEntity, parent_body: &HashMap<String, Value>, children: &[GraphChild], schema_override: Option<&str>, rls_tenant_id: Option<&str>, set_local_sql: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<(Value, HashMap<String, Vec<Value>>), AppError>
Insert a parent row and its FK-children atomically in a single transaction.
children pairs each ToMany include spec with its resolved child entity and the
list of child bodies to insert. For every child, the FK column (spec.their_key_column)
is set to the parent’s spec.our_key_column value before insertion. Any error rolls
the whole transaction back, so no orphan parent or partial child set is ever committed.
set_local_sql is the SET LOCAL app.tenant_id = '...' statement for RLS tenants; it is
run inside the transaction so it scopes to these inserts. Pass None for pool strategy.
Returns (parent_row, child_rows_by_include_name) as raw DB rows (snake_case keys,
sensitive columns NOT stripped — the caller shapes the response).
Sourcepub async fn update<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
id: &Value,
body: &HashMap<String, Value>,
schema_override: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Option<Value>, AppError>
pub async fn update<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, id: &Value, body: &HashMap<String, Value>, schema_override: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<Option<Value>, AppError>
Update one row by id. Returns updated row. When caller_user_id is Some, updated_by is set to that value. When entity has versioning enabled, a history snapshot is written atomically before the update.
Sourcepub async fn delete<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
id: &Value,
schema_override: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Option<Value>, AppError>
pub async fn delete<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, id: &Value, schema_override: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<Option<Value>, AppError>
Delete one row by id. Returns deleted row or None. When caller_user_id is Some, audit_by is set on the audit record. When entity has versioning enabled, a history snapshot is written atomically before the delete.
Sourcepub async fn archive<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
archive_field: &str,
id: &Value,
schema_override: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Option<Value>, AppError>
pub async fn archive<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, archive_field: &str, id: &Value, schema_override: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<Option<Value>, AppError>
Archive one row by id: stamps archive_field with NOW() if it is currently NULL.
Returns the updated row, or None if the record was not found or already archived.
When audit_log is enabled, records an "archive" audit entry for the resulting row.
When caller_user_id is Some, audit_by is set on the audit record, and updated_by is
stamped on the row itself (when the entity has an updated_by column). updated_at is
always stamped when the entity has that column.
Sourcepub async fn unarchive<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
archive_field: &str,
id: &Value,
schema_override: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Option<Value>, AppError>
pub async fn unarchive<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, archive_field: &str, id: &Value, schema_override: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<Option<Value>, AppError>
Unarchive one row by id: clears archive_field (sets to NULL) if it is currently NOT NULL.
Returns the updated row, or None if the record was not found or not archived.
When audit_log is enabled, records an "unarchive" audit entry for the resulting row.
When caller_user_id is Some, audit_by is set on the audit record, and updated_by is
stamped on the row itself (when the entity has an updated_by column). updated_at is
always stamped when the entity has that column.
Sourcepub async fn bulk_create<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
items: &[HashMap<String, Value>],
schema_override: Option<&str>,
rls_tenant_id: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Vec<Value>, AppError>
pub async fn bulk_create<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, items: &[HashMap<String, Value>], schema_override: Option<&str>, rls_tenant_id: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<Vec<Value>, AppError>
Bulk create in a transaction (when using pool) or on the same connection (when using conn). Returns vec of created rows.
When rls_tenant_id is Some (RLS strategy), tenant_id column is set automatically on each row.
When caller_user_id is Some, created_by is set on each row.
Each row is created via Self::create on the shared transaction/connection, so audit_log
rows are honored exactly as for single creates.
Sourcepub async fn bulk_create_collecting<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
items: &[HashMap<String, Value>],
schema_override: Option<&str>,
rls_tenant_id: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<(Vec<Value>, Vec<(usize, AppError)>), AppError>
pub async fn bulk_create_collecting<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, items: &[HashMap<String, Value>], schema_override: Option<&str>, rls_tenant_id: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<(Vec<Value>, Vec<(usize, AppError)>), AppError>
Like bulk_create but uses savepoints to isolate per-row DB errors.
Returns (successful_rows, row_errors). If any errors occur the transaction is
rolled back and successful_rows will be empty — call site decides how to surface errors.
Each row is created via Self::create on the shared transaction/connection, so audit_log
rows are honored exactly as for single creates (and roll back with their row on error).
Sourcepub async fn bulk_update<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
items: &[HashMap<String, Value>],
schema_override: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<Vec<Value>, AppError>
pub async fn bulk_update<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, items: &[HashMap<String, Value>], schema_override: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<Vec<Value>, AppError>
Bulk update in a transaction (when using pool) or on the same connection (when using conn). Each item must have id. Returns vec of updated rows.
When caller_user_id is Some, updated_by is set on each row.
Each row is updated via Self::update on the shared transaction/connection, so audit_log
rows and versioning snapshots are honored exactly as for single updates.
Sourcepub async fn bulk_update_collecting<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
items: &[HashMap<String, Value>],
schema_override: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<(Vec<Value>, Vec<(usize, AppError)>), AppError>
pub async fn bulk_update_collecting<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, items: &[HashMap<String, Value>], schema_override: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<(Vec<Value>, Vec<(usize, AppError)>), AppError>
Like bulk_update but uses savepoints to isolate per-row DB errors.
Missing pk on an item is recorded as a row error rather than aborting early.
Returns (successful_rows, row_errors). If any errors occur the transaction is
rolled back and successful_rows will be empty.
Each row is updated via Self::update on the shared transaction/connection, so audit_log
rows and versioning snapshots are honored exactly as for single updates (and roll back with
their row on error).
Sourcepub async fn bulk_delete_collecting<'a>(
executor: &mut TenantExecutor<'a>,
entity: &ResolvedEntity,
ids: &[Value],
schema_override: Option<&str>,
caller_user_id: Option<&str>,
dialect: &dyn Dialect,
) -> Result<(Vec<Value>, Vec<(usize, AppError)>), AppError>
pub async fn bulk_delete_collecting<'a>( executor: &mut TenantExecutor<'a>, entity: &ResolvedEntity, ids: &[Value], schema_override: Option<&str>, caller_user_id: Option<&str>, dialect: &dyn Dialect, ) -> Result<(Vec<Value>, Vec<(usize, AppError)>), AppError>
Bulk delete by id with per-row savepoint isolation, mirroring bulk_update_collecting.
Each id is deleted via Self::delete on the shared transaction/connection, so versioning
snapshots and audit rows are honored exactly as for single deletes. Ids that do not exist are
skipped silently (no row and no error), matching single-delete semantics.
Returns (deleted_rows, row_errors). If any error occurs the transaction is rolled back and
deleted_rows is cleared (all-or-nothing).
Sourcepub async fn query_history_many<'a>(
executor: &mut TenantExecutor<'a>,
sql: &str,
params: &[Value],
) -> Result<Vec<Value>, AppError>
pub async fn query_history_many<'a>( executor: &mut TenantExecutor<'a>, sql: &str, params: &[Value], ) -> Result<Vec<Value>, AppError>
Execute a history SELECT that returns multiple rows (used by list_history handler). Binds: params[0] = pk value.
Sourcepub async fn query_history_one<'a>(
executor: &mut TenantExecutor<'a>,
sql: &str,
id: &Value,
version: i64,
) -> Result<Option<Value>, AppError>
pub async fn query_history_one<'a>( executor: &mut TenantExecutor<'a>, sql: &str, id: &Value, version: i64, ) -> Result<Option<Value>, AppError>
Execute a history SELECT that returns one row (used by read_history_version handler). Binds: $1 = pk value, $2 = version (i64).
Sourcepub async fn run_readonly_query<'a>(
executor: &mut TenantExecutor<'a>,
sql: &str,
params: &[Value],
) -> Result<Vec<Value>, AppError>
pub async fn run_readonly_query<'a>( executor: &mut TenantExecutor<'a>, sql: &str, params: &[Value], ) -> Result<Vec<Value>, AppError>
Execute an arbitrary read-only query with positional params, returning rows as JSON.
Used by the reports feature. The caller is responsible for opening a read-only, sandboxed
transaction (SET TRANSACTION READ ONLY + statement_timeout, and RLS app.tenant_id) and
passing a TenantExecutor::conn bound to it; this method only binds params and serializes
rows via the same row_to_json used for entity CRUD.
Auto Trait Implementations§
impl Freeze for CrudService
impl RefUnwindSafe for CrudService
impl Send for CrudService
impl Sync for CrudService
impl Unpin for CrudService
impl UnsafeUnpin for CrudService
impl UnwindSafe for CrudService
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more