use ciborium::Value as CborValue;
use vantage_core::{Result, error};
use vantage_table::column::core::Column;
use vantage_table::sorting::{OrderBy, SortDirection as TableSortDirection};
use vantage_table::traits::table_source::TableSource as _;
use vantage_vista::{
Capability, Condition, FilterOp, SortDirection, condition_column_error, operand_text,
unsupported,
};
use crate::graphql::impls::get_by_id::{id_for_column, is_integer_column};
use crate::graphql::impls::search::contains_pattern;
use crate::graphql::operation::GraphqlOperation;
use crate::graphql::types::AnyGraphqlType;
use crate::graphql::vista::source::GraphqlApiTableShell;
impl GraphqlApiTableShell {
pub(crate) fn apply_condition(&mut self, condition: &Condition) -> Result<()> {
let field = condition.column.as_str();
if field.contains('.')
&& self.table.data_source().can_filter()
&& !self.table.columns().contains_key(field)
{
return Err(condition_column_error(&self.metadata, field));
}
let op = condition.op;
if op != FilterOp::Eq && !self.capabilities.has(Capability::FilterOps) {
return unsupported(Capability::FilterOps);
}
let column = Column::<AnyGraphqlType>::new(&condition.column);
let scalar = || self.operand(field, &condition.value);
let expression = match op {
FilterOp::Eq => column.eq(scalar()?),
FilterOp::Ne => column.ne(scalar()?),
FilterOp::Gt => column.gt(scalar()?),
FilterOp::Gte => column.gte(scalar()?),
FilterOp::Lt => column.lt(scalar()?),
FilterOp::Lte => column.lte(scalar()?),
FilterOp::InSet | FilterOp::NotInSet => {
let items = match &condition.value {
CborValue::Array(items) => items
.iter()
.map(|item| self.operand(field, item))
.collect::<Result<Vec<_>>>()?,
single => vec![self.operand(field, single)?],
};
if op == FilterOp::InSet {
column.in_(items)
} else {
column.not_in(items)
}
}
FilterOp::Like => column.ilike(contains_pattern(&operand_text(&condition.value))),
_ => return unsupported(Capability::FilterOps),
};
self.table.add_condition(expression);
Ok(())
}
fn operand(&self, field: &str, value: &CborValue) -> Result<AnyGraphqlType> {
let column = self.table.columns().get(field);
match value {
CborValue::Text(text) if is_integer_column(column) => {
Ok(AnyGraphqlType::untyped(id_for_column(column, text, field)?))
}
other => Ok(AnyGraphqlType::from(other.clone())),
}
}
pub(crate) fn apply_search(&mut self, text: Option<&str>) -> Result<()> {
let condition = text.map(|text| {
self.table
.data_source()
.search_table_condition(&self.table, text)
});
self.search_handle = self
.table
.replace_temp_condition(self.search_handle, condition)?;
Ok(())
}
pub(crate) fn apply_order(&mut self, order: Option<(&str, SortDirection)>) -> Result<()> {
let Some((field, dir)) = order else {
self.table.clear_orders();
return Ok(());
};
if !self.table.columns().contains_key(field) {
return Err(error!("Unknown column for order", field = field));
}
if field.contains('.') {
return Err(
error!("GraphQL cannot order by a dotted column", field = field).mark_unsupported(),
);
}
self.table.clear_orders();
let direction = match dir {
SortDirection::Ascending => TableSortDirection::Ascending,
SortDirection::Descending => TableSortDirection::Descending,
};
self.table.add_order(OrderBy {
expression: Column::<AnyGraphqlType>::new(field).eq(field),
direction,
});
Ok(())
}
}