use ciborium::Value as CborValue;
use vantage_core::{Context, Result, error};
use vantage_table::table::Table;
use vantage_types::{EmptyEntity, Record};
use vantage_vista::{ReferenceKind, Vista};
use crate::graphql::api::GraphqlApi;
use crate::graphql::impls::get_by_id::id_for_column;
use crate::graphql::types::AnyGraphqlType;
use crate::graphql::vista::factory::{GraphqlApiVistaFactory, wrap_spec_table};
use crate::graphql::vista::lowering::{TargetSpec, build_table};
use crate::graphql::vista::source::GraphqlApiTableShell;
pub(crate) type BareTarget = Table<GraphqlApi, EmptyEntity>;
impl GraphqlApiTableShell {
pub(crate) fn spec_target(&self, relation: &str) -> Result<Option<(TargetSpec, BareTarget)>> {
let (Some(resolver), Some(reference)) =
(&self.resolver, self.metadata.references.get(relation))
else {
return Ok(None);
};
let target_name = reference.target.as_str();
let target = resolver(target_name).ok_or_else(|| {
error!(
"GraphQL reference target has no registered spec; register it with \
`register_yaml` or resolve it through `with_resolver`",
relation = relation,
target = target_name
)
})?;
let bare =
build_table(&target.spec, &target.api, Some(resolver.clone())).with_context(|| {
error!(
"GraphQL reference target spec can't be built",
relation = relation,
target = target_name
)
})?;
Ok(Some((target, bare)))
}
pub(crate) fn coerce_join_value(
&self,
relation: &str,
target: &BareTarget,
row: &Record<CborValue>,
native: &mut Record<AnyGraphqlType>,
) -> Result<()> {
let Some(reference) = self
.table
.vista_references()
.into_iter()
.find(|r| r.name == relation)
else {
return Ok(());
};
let (source, target_column) = match reference.kind {
ReferenceKind::HasOne => (reference.foreign_key, target.id_field()),
ReferenceKind::HasMany => {
let column = target.columns().get(&reference.foreign_key);
(self.table.id_field_name(), column)
}
};
if let Some(CborValue::Text(text)) = row.get(&source) {
let value = id_for_column(target_column, text, &source)?;
native.insert(source, AnyGraphqlType::untyped(value));
}
Ok(())
}
pub(crate) fn wrap_target(
&self,
table: Table<GraphqlApi, EmptyEntity>,
target: Option<TargetSpec>,
) -> Result<Vista> {
match (target, &self.resolver) {
(Some(target), Some(resolver)) => {
Ok(wrap_spec_table(table, &target.spec, resolver.clone()))
}
_ => GraphqlApiVistaFactory::new(self.table.data_source().clone()).from_table(table),
}
}
}