fraiseql_core/federation/
database_resolver.rs1use std::sync::Arc;
7
8use serde_json::Value;
9use tracing::warn;
10
11use crate::{
12 db::traits::DatabaseAdapter,
13 error::Result,
14 federation::{
15 metadata_helpers::find_federation_type,
16 query_builder::construct_where_in_clause,
17 requires_provides_validator::RequiresProvidesRuntimeValidator,
18 selection_parser::FieldSelection,
19 tracing::FederationTraceContext,
20 types::{EntityRepresentation, FederatedType, FederationMetadata},
21 },
22};
23
24pub struct DatabaseEntityResolver<A: DatabaseAdapter> {
26 adapter: Arc<A>,
28 metadata: FederationMetadata,
30}
31
32impl<A: DatabaseAdapter> DatabaseEntityResolver<A> {
33 #[must_use]
35 pub fn new(adapter: Arc<A>, metadata: FederationMetadata) -> Self {
36 Self { adapter, metadata }
37 }
38
39 pub async fn resolve_entities_from_db(
55 &self,
56 typename: &str,
57 representations: &[EntityRepresentation],
58 selection: &FieldSelection,
59 ) -> Result<Vec<Option<Value>>> {
60 self.resolve_entities_from_db_with_tracing(typename, representations, selection, None)
61 .await
62 }
63
64 pub async fn resolve_entities_from_db_with_tracing(
81 &self,
82 typename: &str,
83 representations: &[EntityRepresentation],
84 selection: &FieldSelection,
85 _trace_context: Option<FederationTraceContext>,
86 ) -> Result<Vec<Option<Value>>> {
87 if representations.is_empty() {
88 return Ok(Vec::new());
89 }
90
91 let fed_type = find_federation_type(typename, &self.metadata)?;
93
94 let table_name = typename.to_lowercase();
96
97 let where_clause = construct_where_in_clause(typename, representations, &self.metadata)?;
99
100 let mut select_fields = selection.fields.clone();
102 for key in &fed_type.keys {
103 for field in &key.fields {
104 if !select_fields.contains(field) {
105 select_fields.push(field.clone());
106 }
107 }
108 }
109
110 if !select_fields.contains(&"__typename".to_string()) {
112 select_fields.push("__typename".to_string());
113 }
114
115 let sql = format!(
117 "SELECT {} FROM {} WHERE {}",
118 select_fields.join(", "),
119 table_name,
120 where_clause
121 );
122
123 let rows = self.adapter.execute_raw_query(&sql).await?;
125
126 project_results(&rows, representations, fed_type, typename)
128 }
129}
130
131fn project_results(
133 rows: &[std::collections::HashMap<String, Value>],
134 representations: &[EntityRepresentation],
135 fed_type: &FederatedType,
136 typename: &str,
137) -> Result<Vec<Option<Value>>> {
138 use std::collections::HashMap as StdHashMap;
139
140 let mut row_map: StdHashMap<Vec<String>, StdHashMap<String, Value>> = StdHashMap::new();
143
144 for row in rows {
145 let key_values: Result<Vec<String>> = fed_type
147 .keys
148 .first()
149 .ok_or_else(|| crate::error::FraiseQLError::Validation {
150 message: format!("Type '{}' has no key fields", typename),
151 path: None,
152 })?
153 .fields
154 .iter()
155 .map(|field| {
156 row.get(field)
157 .and_then(|v| v.as_str().map(|s| s.to_string()))
158 .or_else(|| row.get(field).map(|v| v.to_string()))
159 .ok_or_else(|| crate::error::FraiseQLError::Validation {
160 message: format!("Key field '{}' not found in row", field),
161 path: None,
162 })
163 })
164 .collect();
165
166 if let Ok(key) = key_values {
167 row_map.insert(key, row.clone());
168 }
169 }
170
171 let mut results = Vec::new();
173 for rep in representations {
174 let key_values: Vec<String> = fed_type
176 .keys
177 .first()
178 .map(|k| {
179 k.fields
180 .iter()
181 .filter_map(|field| {
182 rep.key_fields.get(field).and_then(|v| {
183 v.as_str().map(|s| s.to_string()).or_else(|| Some(v.to_string()))
184 })
185 })
186 .collect()
187 })
188 .unwrap_or_default();
189
190 if let Some(row) = row_map.get(&key_values) {
192 let mut entity = row.clone();
193 entity.insert("__typename".to_string(), Value::String(typename.to_string()));
194
195 if let Err(validation_errors) =
197 RequiresProvidesRuntimeValidator::validate_entity_against_type(
198 typename, &entity, fed_type,
199 )
200 {
201 for error in validation_errors {
203 warn!("Federation directive validation error for {}: {}", typename, error);
204 }
205 }
206
207 results.push(Some(Value::Object(serde_json::Map::from_iter(entity))));
208 } else {
209 results.push(None);
211 }
212 }
213
214 Ok(results)
215}
216
217#[cfg(test)]
218mod tests {
219 #[test]
220 fn test_database_resolver_creation() {
221 }
224}