diff --git a/packages/fuel-indexer-graphql/src/query.rs b/packages/fuel-indexer-graphql/src/query.rs index 2478eaf01..0570409e9 100644 --- a/packages/fuel-indexer-graphql/src/query.rs +++ b/packages/fuel-indexer-graphql/src/query.rs @@ -18,7 +18,7 @@ use crate::{ GraphqlError, GraphqlResult, }; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CommonTable { pub name: String, pub prepared_operation: PreparedOperation, @@ -224,7 +224,7 @@ impl DependencyGraph { /// PreparedOperation contains all of the necessary operation information to /// generate a correct SQL query. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PreparedOperation { pub selection_set: PreparedSelection, pub ctes: Vec, @@ -451,7 +451,6 @@ impl std::fmt::Display for Object { pub struct Root { root_entity: String, fields: Vec, - response_kind: QueryResponseKind, } impl std::fmt::Display for Root { @@ -509,22 +508,16 @@ impl PreparedSelection { PreparedSelection::List(_) => Err(GraphqlError::QueryError( "List cannot have a root name".to_string(), )), - PreparedSelection::Object(o) => { - if let Some(name) = &o.name { - Ok(name.to_string()) - } else { - Err(GraphqlError::QueryError( - "Object does not have a root name".to_string(), - )) - } - } + PreparedSelection::Object(_) => Err(GraphqlError::QueryError( + "Object cannot have a root name".to_string(), + )), PreparedSelection::Root(r) => Ok(r.root_entity.to_lowercase()), } } } #[derive(Debug, Clone)] -pub enum QueryResponseKind { +pub enum QueryKind { Object, Connection, } @@ -552,12 +545,12 @@ pub enum SelectionNode { node: Box, obj_type: String, }, - RootNode { + QueryRoot { name: Name, alias: Option>, fields: Vec, arguments: Vec, - response_kind: QueryResponseKind, + kind: QueryKind, root_entity_type: String, }, PageInfo { @@ -566,20 +559,10 @@ pub enum SelectionNode { fields: Vec, parent_entity: String, }, - Connection { - name: Name, - alias: Option>, - entity: String, - fields: Vec, - arguments: Vec, - parent_obj: Option, - }, Edge { name: Name, - parent_entity: String, - alias: Option>, - fields: Vec, cursor: Box>, + node: Box>, entity: String, }, } @@ -591,9 +574,8 @@ impl SelectionNode { SelectionNode::Scalar { name, .. } => name.to_string(), SelectionNode::Object { name, .. } => name.to_string(), SelectionNode::List { name, .. } => name.to_string(), - SelectionNode::RootNode { name, .. } => name.to_string(), + SelectionNode::QueryRoot { name, .. } => name.to_string(), SelectionNode::PageInfo { name, .. } => name.to_string(), - SelectionNode::Connection { name, .. } => name.to_string(), SelectionNode::Edge { name, .. } => name.to_string(), } } @@ -788,17 +770,17 @@ impl SelectionNode { Ok(PreparedSelection::List(list)) } - SelectionNode::RootNode { + SelectionNode::QueryRoot { name, alias, fields, arguments, - response_kind, + kind, root_entity_type, } => { let mut obj_fields: Vec = vec![]; - match response_kind { - QueryResponseKind::Object => { + match kind { + QueryKind::Object => { query_parameters.add_params( arguments.to_owned(), format!( @@ -879,12 +861,11 @@ impl SelectionNode { let object = Root { fields: obj_fields, root_entity: root_entity_type.to_string(), - response_kind: response_kind.clone(), }; Ok(PreparedSelection::Root(object)) } - QueryResponseKind::Connection => { + QueryKind::Connection => { let mut cte_dep_graph = DependencyGraph { fully_qualified_namespace: schema .fully_qualified_namespace(), @@ -917,17 +898,18 @@ impl SelectionNode { obj_fields.push(prepared_selection); } - let selection_set = PreparedSelection::Object(Object { - name: Some(field_name), - fields: obj_fields, - }); + let prepared_cte_query_root = + PreparedSelection::Root(Root { + root_entity: root_entity_type.to_string(), + fields: obj_fields, + }); let (aggregate_func_used, group_by_fields) = - get_fields_from_selection(&selection_set); + get_fields_from_selection(&prepared_cte_query_root); - let op = PreparedOperation { - selection_set: selection_set.clone(), - ctes: vec![], + let cte_op = PreparedOperation { + selection_set: prepared_cte_query_root, + ctes: common_tables.clone(), group_by_fields, fully_qualified_namespace: schema .fully_qualified_namespace(), @@ -940,107 +922,39 @@ impl SelectionNode { let cte = CommonTable { name: name.to_string(), - prepared_operation: op, + prepared_operation: cte_op, }; + let selection = PreparedSelection::Field(Field { + name: field_name, + path: format!( + "{}.{}", + cte.name.clone(), + cte.name.clone() + ), + }); + + let query_root = PreparedSelection::Root(Root { + root_entity: root_entity_type.to_string(), + fields: vec![selection], + }); + common_tables.push(cte); - Ok(selection_set) + Ok(query_root) } } } SelectionNode::PageInfo { .. } => unimplemented!(), - SelectionNode::Connection { - name, - alias, - entity, - fields, - arguments, - parent_obj, - } => { - let mut cte_dep_graph = DependencyGraph::default(); - if let Some(parent_entity) = parent_obj { - if let Some(fk_map) = schema - .foreign_key_mappings() - .get(&parent_entity.to_lowercase()) - { - let key = name.replace("Connection", ""); - if let Some((fk_table, fk_field)) = - fk_map.get(&key.to_lowercase()) - { - let outer_obj_node = cte_dep_graph.add_node(format!( - "{}.{}", - schema.fully_qualified_namespace(), - parent_entity.to_lowercase() - )); - let inner_obj_node = cte_dep_graph.add_node(format!( - "{}.{}", - schema.fully_qualified_namespace(), - fk_table.clone() - )); - let connecting_node = - cte_dep_graph.add_node(format!( - "{}.{}s_{}s", - schema.fully_qualified_namespace(), - parent_entity.to_lowercase(), - entity.to_lowercase(), - )); - cte_dep_graph.add_edge( - outer_obj_node, - connecting_node, - fk_field.clone(), - format!( - "{}_{fk_field}", - parent_entity.to_lowercase() - ), - ); - cte_dep_graph.add_edge( - connecting_node, - inner_obj_node, - format!("{}_{fk_field}", entity.to_lowercase()), - fk_field.clone(), - ); - } - } - } - let field_name = alias - .clone() - .map_or(format!("'{}'", name), |a| format!("'{}'", a.node)); - - query_parameters.add_params( - arguments.to_owned(), - format!("{}.{}", schema.fully_qualified_namespace(), entity), - ); - - let mut obj_fields: Vec = vec![]; - for sn in fields { - let prepared_selection = sn.prepare( - schema, - db_type, - &mut cte_dep_graph, - query_parameters, - common_tables, - )?; - obj_fields.push(prepared_selection); - } - - let object = Object { - name: Some(field_name), - fields: obj_fields, - }; - - Ok(PreparedSelection::Object(object)) - } SelectionNode::Edge { name: _, - parent_entity: _, - alias: _, - fields, cursor, entity, + node, } => { - let mut obj_fields: Vec = if cursor.is_some() { - let field = Field { + let mut obj_fields = vec![]; + if cursor.is_some() { + let cursor_field = Field { name: "'cursor'".to_string(), path: format!( "{}.{}.id", @@ -1048,20 +962,65 @@ impl SelectionNode { entity.to_lowercase() ), }; - vec![PreparedSelection::Field(field)] - } else { - vec![] - }; - for sn in fields { - let prepared_selection = sn.prepare( - schema, - db_type, - dependency_graph, - query_parameters, - common_tables, - )?; - obj_fields.push(prepared_selection); + obj_fields.push(PreparedSelection::Field(cursor_field)); + } + + // TODO: Replace this with an object/node struct so we don't have to do if/let + if let Some(SelectionNode::Object { + name, + parent_entity, + alias, + fields, + is_part_of_list, + arguments, + entity_type, + }) = *node.clone() + { + for selection_node in fields { + if let SelectionNode::List { + name, + alias, + node: list_node, + obj_type, + } = selection_node + { + if let SelectionNode::Object { + name, + parent_entity, + alias, + fields, + is_part_of_list, + arguments, + entity_type, + } = *list_node + { + // TODO: + // This is a list of objects inside an Edge type, + // we need to make a CTE for this nested object + } else { + let prepared_selection = list_node.prepare( + schema, + db_type, + dependency_graph, + query_parameters, + common_tables, + )?; + obj_fields.push(prepared_selection); + } + // manually parse it and do something similar to CTE work above + } else { + let prepared_selection = selection_node.prepare( + schema, + db_type, + dependency_graph, + query_parameters, + common_tables, + )?; + obj_fields.push(prepared_selection); + } + } } + // TODO: Alias? let object = Object { name: None, @@ -1173,31 +1132,33 @@ fn parse_selections( }; match t { - InternalType::Connection => SelectionNode::Connection { - name: f.node.name.node.clone(), - alias: f.node.alias.clone(), - entity, - fields, - arguments, - parent_obj: parent_obj.cloned(), - }, InternalType::Edge => { - let (cursor, fields) = if let Some(idx) = + let cursor = if let Some(idx) = fields.iter().position(|f| f.name() == *"cursor") { let c = fields.swap_remove(idx); - (Box::new(Some(c)), fields) + Box::new(Some(c)) + } else { + Box::new(None) + }; + + let node = if let Some(idx) = + fields.iter().position(|f| f.name() == *"node") + { + let n = fields.swap_remove(idx); + Box::new(Some(n)) } else { - (Box::new(None), fields) + Box::new(None) }; SelectionNode::Edge { name: f.node.name.node.clone(), - alias: f.node.alias.clone(), - parent_entity: parent.to_string(), - fields, + // alias: f.node.alias.clone(), + // parent_entity: parent.to_string(), + // fields, cursor, entity, + node, } } InternalType::PageInfo => { @@ -1218,6 +1179,9 @@ fn parse_selections( parent_entity: backing_obj, } } + _ => { + return Err(GraphqlError::QueryError("FIXME".to_string())) + } } } else if has_no_subselections { SelectionNode::Scalar { @@ -1281,23 +1245,19 @@ fn parse_selections( schema, Some(&query_type), )?; - let (response_kind, query_type) = - if query_type.contains("Connection") { - ( - QueryResponseKind::Connection, - query_type.replace("Connection", ""), - ) - } else { - (QueryResponseKind::Object, query_type) - }; + let (kind, query_type) = if query_type.contains("Connection") { + (QueryKind::Connection, query_type.replace("Connection", "")) + } else { + (QueryKind::Object, query_type) + }; - v.push(SelectionNode::RootNode { + v.push(SelectionNode::QueryRoot { name: f.node.name.node.clone(), alias: f.node.alias.clone(), fields, arguments, root_entity_type: query_type, - response_kind, + kind, }); return Ok(v); } else {