use super::{Executor, runners};
use crate::{
db::traits::{DatabaseAdapter, SupportsMutations},
error::{FraiseQLError, Result},
graphql::FieldSelection,
security::SecurityContext,
};
impl<A: DatabaseAdapter + SupportsMutations> Executor<A> {
fn mutation_runner(&self) -> runners::mutation::MutationRunner<A> {
runners::mutation::MutationRunner::new(std::sync::Arc::clone(&self.ctx))
}
pub async fn execute_mutation(
&self,
mutation_name: &str,
variables: Option<&serde_json::Value>,
selections: &[FieldSelection],
) -> Result<serde_json::Value> {
self.mutation_runner()
.execute_mutation(mutation_name, variables, selections)
.await
}
}
impl<A: DatabaseAdapter> Executor<A> {
pub(super) async fn execute_mutation_query(
&self,
mutation_name: &str,
variables: Option<&serde_json::Value>,
security_context: Option<&SecurityContext>,
selections: &[FieldSelection],
inline_arguments: &[crate::graphql::GraphQLArgument],
) -> Result<serde_json::Value> {
if !self.ctx.adapter.supports_mutations() {
return Err(FraiseQLError::Validation {
message: format!(
"Mutation '{mutation_name}' cannot be executed: the configured database \
adapter does not support mutations. Use PostgresAdapter, MySqlAdapter, \
or SqlServerAdapter for mutation operations."
),
path: None,
});
}
runners::mutation::execute_mutation_impl(
&self.ctx,
mutation_name,
variables,
security_context,
selections,
inline_arguments,
)
.await
}
pub async fn execute_mutation_with_security(
&self,
mutation_name: &str,
arguments: &serde_json::Value,
security_context: Option<&SecurityContext>,
) -> crate::error::Result<serde_json::Value> {
let args_str = if let Some(obj) = arguments.as_object() {
obj.iter().map(|(k, v)| format!("{k}: {v}")).collect::<Vec<_>>().join(", ")
} else {
String::new()
};
let query = if args_str.is_empty() {
format!("mutation {{ {mutation_name} {{ status entity_id message }} }}")
} else {
format!("mutation {{ {mutation_name}({args_str}) {{ status entity_id message }} }}")
};
if let Some(ctx) = security_context {
self.execute_with_security(&query, None, ctx).await
} else {
self.execute(&query, None).await
}
}
pub async fn execute_mutation_batch(
&self,
mutation_name: &str,
items: &[serde_json::Value],
security_context: Option<&SecurityContext>,
) -> crate::error::Result<crate::runtime::BulkResult> {
let mut entities = Vec::with_capacity(items.len());
for item in items {
let result = self
.execute_mutation_with_security(mutation_name, item, security_context)
.await?;
entities.push(result);
}
Ok(crate::runtime::BulkResult {
affected_rows: entities.len() as u64,
entities: Some(entities),
})
}
pub async fn execute_bulk_by_filter(
&self,
query_match: &crate::runtime::matcher::QueryMatch,
mutation_name: &str,
body: Option<&serde_json::Value>,
_id_field: &str,
_max_affected: u64,
security_context: Option<&SecurityContext>,
) -> crate::error::Result<crate::runtime::BulkResult> {
let filter_result = self
.query_runner()
.execute_query_direct(query_match, None, security_context)
.await?;
let args = body.cloned().unwrap_or(serde_json::json!({}));
let result = self
.execute_mutation_with_security(mutation_name, &args, security_context)
.await?;
let count = filter_result
.get("data")
.and_then(|d| d.as_object())
.and_then(|o| o.values().next())
.and_then(|v| v.as_array())
.map_or(1, |a| a.len() as u64);
Ok(crate::runtime::BulkResult {
affected_rows: count,
entities: Some(vec![result]),
})
}
}