use std::sync::Arc;
use async_graphql::dynamic::{Field, FieldFuture, FieldValue, InputValue, Object, Type};
use super::GraphqlError;
use super::schema::{graphql_to_sql_kind_with_scope, sql_value_to_graphql_value_with_kind};
use super::utils::execute_plan;
use crate::catalog::FunctionDefinition;
use crate::dbs::Session;
use crate::expr::{Expr, FunctionCall, Kind, LogicalPlan, TopLevelExpr};
use crate::graphql::schema::kind_to_type_with_enum_prefix;
use crate::kvs::Datastore;
use crate::val::Value;
pub async fn process_fns(
fns: Arc<[FunctionDefinition]>,
mut query: Object,
types: &mut Vec<Type>,
datastore: &Arc<Datastore>,
) -> Result<Object, GraphqlError> {
for fnd in fns.iter() {
let Some(kind) = &fnd.returns else {
continue;
};
let kvs1 = Arc::clone(datastore);
let fnd1 = fnd.clone();
let field_name = match fnd.graphql_alias.as_deref() {
Some(alias) if super::tables::is_valid_graphql_identifier_pub(alias) => {
alias.to_string()
}
_ => format!("fn_{}", fnd.name),
};
let mut field = Field::new(
field_name,
kind_to_type_with_enum_prefix(
kind.clone(),
types,
false,
Some(&format!("fn_{}_return", fnd.name)),
)?,
move |ctx| {
let kvs1 = Arc::clone(&kvs1);
let fnd1 = fnd1.clone();
FieldFuture::new(async move {
let sess1 = ctx.data::<Arc<Session>>()?;
let graphql_args = ctx.args.as_index_map();
let mut args = Vec::new();
for (arg_name, arg_kind) in fnd1.args.iter() {
if let Some(arg_val) = graphql_args.get(arg_name.as_str()) {
let scope = format!("fn_{}_{}", fnd1.name, arg_name);
let arg_val = graphql_to_sql_kind_with_scope(
arg_val,
arg_kind.clone(),
Some(&scope),
)?;
args.push(arg_val.into_literal());
} else {
args.push(Value::None.into_literal());
}
}
let func_call = Expr::FunctionCall(Box::new(FunctionCall {
receiver: crate::expr::Function::Custom(fnd1.name.to_string()),
arguments: args,
}));
let plan = LogicalPlan {
expressions: vec![TopLevelExpr::Expr(func_call)],
};
let res = execute_plan(&kvs1, sess1.as_ref(), plan).await?;
let graphql_res = match res {
Value::RecordId(rid) => {
let field_val = FieldValue::owned_any(rid.clone());
let field_val = match &fnd1.returns {
Some(Kind::Record(ts)) if ts.is_empty() => {
field_val.with_type(rid.table)
}
_ => field_val,
};
Some(field_val)
}
Value::None => None,
_ => Some(FieldValue::value(sql_value_to_graphql_value_with_kind(
res,
fnd1.returns.as_ref(),
Some(&format!("fn_{}_return", fnd1.name)),
)?)),
};
Ok(graphql_res)
})
},
);
if let Some(desc) = super::naming::description_with_deprecation(
fnd.comment.as_deref(),
fnd.graphql_deprecated.as_deref(),
) {
field = field.description(desc);
}
for (arg_name, arg_kind) in fnd.args.iter() {
let arg_ty = kind_to_type_with_enum_prefix(
arg_kind.clone(),
types,
true,
Some(&format!("fn_{}_{}", fnd.name, arg_name)),
)?;
field = field.argument(InputValue::new(arg_name, arg_ty))
}
query = query.field(field);
}
Ok(query)
}