use fraiseql_db::ViewName;
use crate::schema::{CompiledSchema, MutationDefinition};
#[must_use]
pub fn view_of_type(schema: &CompiledSchema, type_name: &str) -> Option<ViewName> {
schema
.types
.iter()
.find(|t| t.name == type_name)
.filter(|t| !t.sql_source.as_str().is_empty())
.map(|t| ViewName::from(t.sql_source.as_str()))
}
#[must_use]
pub fn payload_entity_type(payload_type: &str, schema: &CompiledSchema) -> Option<String> {
schema
.find_type(payload_type)?
.fields
.iter()
.find(|f| f.name.as_str() == "entity")?
.field_type
.type_name()
.map(std::string::ToString::to_string)
}
#[must_use]
pub fn statically_resolved_views(
mutation: &MutationDefinition,
schema: &CompiledSchema,
) -> Vec<ViewName> {
let mut views: Vec<ViewName> = Vec::new();
let push = |view: ViewName, views: &mut Vec<ViewName>| {
if !views.iter().any(|v| v.as_str() == view.as_str()) {
views.push(view);
}
};
for declared in &mutation.invalidates_views {
push(ViewName::from(declared.as_str()), &mut views);
}
if let Some(view) = view_of_type(schema, &mutation.return_type) {
push(view, &mut views);
}
if let Some(inner) = payload_entity_type(&mutation.return_type, schema) {
if let Some(view) = view_of_type(schema, &inner) {
push(view, &mut views);
}
}
views
}
#[must_use]
pub fn declares_cacheable_views(schema: &CompiledSchema) -> bool {
schema
.queries
.iter()
.any(|q| q.cache_ttl_seconds.is_some() && q.sql_source.is_some())
}
#[must_use]
pub fn unattributable_mutations(schema: &CompiledSchema) -> Vec<&str> {
schema
.mutations
.iter()
.filter(|m| statically_resolved_views(m, schema).is_empty())
.map(|m| m.name.as_str())
.collect()
}
#[cfg(test)]
#[path = "mutation_reach_tests.rs"]
mod mutation_reach_tests;