From 920726b434126964b3e0c0d6ca65c3204acbaa6c Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 24 Nov 2023 00:35:37 -0500 Subject: [PATCH] Push up some WIP work so I can work on it during travel --- .../schema/hello_world.schema.graphql | 9 ++ packages/fuel-indexer-lib/src/graphql/mod.rs | 145 ++++++------------ .../fuel-indexer-lib/src/graphql/parser.rs | 11 +- 3 files changed, 66 insertions(+), 99 deletions(-) diff --git a/examples/hello-world/hello-world/schema/hello_world.schema.graphql b/examples/hello-world/hello-world/schema/hello_world.schema.graphql index 6200b96ca..ff1a5fa6c 100644 --- a/examples/hello-world/hello-world/schema/hello_world.schema.graphql +++ b/examples/hello-world/hello-world/schema/hello_world.schema.graphql @@ -1,3 +1,12 @@ +schema { + query: Query +} + +type Query { + block(id: ID!): Block + blocks(height: u64!): [Block] +} + type Transaction @entity { id: ID! hash: Bytes32! @unique diff --git a/packages/fuel-indexer-lib/src/graphql/mod.rs b/packages/fuel-indexer-lib/src/graphql/mod.rs index e1ac5ec57..0eb9f4127 100644 --- a/packages/fuel-indexer-lib/src/graphql/mod.rs +++ b/packages/fuel-indexer-lib/src/graphql/mod.rs @@ -16,7 +16,7 @@ use async_graphql_parser::{ }; use fuel_indexer_types::graphql::IndexMetadata; use sha2::{Digest, Sha256}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use types::IdCol; /// Maximum amount of foreign key list fields that can exist on a `TypeDefinition` @@ -39,6 +39,25 @@ fn inject_native_entities_into_schema(schema: &str) -> String { } } +pub(crate) fn inject_query_type(mut ast: ServiceDocument) -> ServiceDocument { + let mut queries: Vec<(&Positioned, &Positioned)> = vec![]; + for ty in ast.definitions.iter() { + if let TypeSystemDefinition::Type(t) = ty { + if let TypeKind::Object(obj) = &t.node.kind { + if !check_for_directive(&t.node.directives, "internal") { + queries.push((&t.node.name, t)); + } else if check_for_directive(&t.node.directives, "internal") + && check_for_directive(&t.node.directives, "connection") + { + } + // if not an internal object, make it a regular query returning one thing + // if an internal connection object, make it a query returning a list + } + } + } + ast +} + /// Inject internal types into the schema. In order to support popular /// functionality (e.g. cursor-based pagination) and minimize the amount /// of types that a user needs to create, internal types are injected into @@ -46,82 +65,24 @@ fn inject_native_entities_into_schema(schema: &str) -> String { /// or entity structs in handler functions. pub(crate) fn inject_internal_types_into_document( mut ast: ServiceDocument, - base_type_names: &HashSet, ) -> ServiceDocument { let mut pagination_types: Vec = Vec::new(); pagination_types.push(create_page_info_type_def()); // Iterate through all objects in document and create special // pagination types for each object with a list field. - for ty in ast.definitions.iter_mut() { + for ty in ast.definitions.iter() { if let TypeSystemDefinition::Type(t) = ty { - if let TypeKind::Object(obj) = &mut t.node.kind { - let mut internal_fields: Vec> = Vec::new(); - - for f in &obj.fields { - if let BaseType::List(inner_type) = &f.node.ty.node.base { - if let BaseType::Named(name) = &inner_type.base { - if base_type_names.contains(&name.to_string()) { - continue; - } - let edge_type = create_edge_type_for_list_field(f); - pagination_types.push(edge_type); - - let connection_type = - create_connection_type_def_for_list_entity(name); - pagination_types.push(connection_type); - - let connection_field = Positioned::position_node( - f, - FieldDefinition { - description: None, - name: Positioned::position_node( - f, - Name::new(format!( - "{}Connection", - f.node.name.node - )), - ), - arguments: vec![], - ty: Positioned::position_node( - f, - Type { - base: BaseType::Named(Name::new(format!( - "{name}Connection" - ))), - nullable: false, - }, - ), - directives: vec![ - Positioned::position_node( - f, - ConstDirective { - name: Positioned::position_node( - f, - Name::new("internal"), - ), - arguments: vec![], - }, - ), - Positioned::position_node( - f, - ConstDirective { - name: Positioned::position_node( - f, - Name::new("connection"), - ), - arguments: vec![], - }, - ), - ], - }, - ); - internal_fields.push(connection_field); - } - } - } + if t.node.name.node == "IndexMetadataEntity" { + continue; + } - obj.fields.append(&mut internal_fields); + if matches!(&t.node.kind, TypeKind::Object(_)) { + let edge_type = create_edge_type(t); + pagination_types.push(edge_type); + + let connection_type = create_connection_type_def(&t.node.name.node); + pagination_types.push(connection_type); } } } @@ -131,32 +92,20 @@ pub(crate) fn inject_internal_types_into_document( ast } -fn create_edge_type_for_list_field( - list_field: &Positioned, -) -> TypeSystemDefinition { - let (base_type, name) = if let BaseType::List(t) = &list_field.node.ty.node.base { - if let BaseType::Named(n) = &t.base { - (t, n) - } else { - unreachable!("Edge type creation should not occur for non-list fields") - } - } else { - unreachable!("Edge type creation should not occur for non-list fields") - }; - +fn create_edge_type(entity_def: &Positioned) -> TypeSystemDefinition { let edge_obj_type = ObjectType { implements: vec![], fields: vec![ Positioned::position_node( - list_field, + entity_def, FieldDefinition { description: None, - name: Positioned::position_node(list_field, Name::new("node")), + name: Positioned::position_node(entity_def, Name::new("node")), arguments: vec![], ty: Positioned::position_node( - list_field, + entity_def, Type { - base: base_type.base.clone(), + base: BaseType::Named(entity_def.node.name.node.clone()), nullable: false, }, ), @@ -164,13 +113,13 @@ fn create_edge_type_for_list_field( }, ), Positioned::position_node( - list_field, + entity_def, FieldDefinition { description: None, - name: Positioned::position_node(list_field, Name::new("cursor")), + name: Positioned::position_node(entity_def, Name::new("cursor")), arguments: vec![], ty: Positioned::position_node( - list_field, + entity_def, Type { base: BaseType::Named(Name::new("String")), nullable: false, @@ -183,29 +132,29 @@ fn create_edge_type_for_list_field( }; TypeSystemDefinition::Type(Positioned::position_node( - list_field, + entity_def, TypeDefinition { extend: false, description: None, name: Positioned::position_node( - list_field, - Name::new(format!("{}Edge", name)), + entity_def, + Name::new(format!("{}Edge", entity_def.node.name.node.clone())), ), directives: vec![ Positioned::position_node( - list_field, + entity_def, ConstDirective { name: Positioned::position_node( - list_field, + entity_def, Name::new("internal"), ), arguments: vec![], }, ), Positioned::position_node( - list_field, + entity_def, ConstDirective { - name: Positioned::position_node(list_field, Name::new("edge")), + name: Positioned::position_node(entity_def, Name::new("edge")), arguments: vec![], }, ), @@ -215,8 +164,8 @@ fn create_edge_type_for_list_field( )) } -/// Generate connection type defintion for a list field on an entity. -fn create_connection_type_def_for_list_entity(name: &Name) -> TypeSystemDefinition { +/// Generate connection type defintion for an entity. +fn create_connection_type_def(name: &Name) -> TypeSystemDefinition { let dummy_position = Pos { line: usize::MAX, column: usize::MAX, diff --git a/packages/fuel-indexer-lib/src/graphql/parser.rs b/packages/fuel-indexer-lib/src/graphql/parser.rs index d13c7dff9..1312162d7 100644 --- a/packages/fuel-indexer-lib/src/graphql/parser.rs +++ b/packages/fuel-indexer-lib/src/graphql/parser.rs @@ -276,6 +276,9 @@ pub struct ParsedGraphQLSchema { /// A mapping of pagination types to the underlying types that they should be used with. pagination_type_map: HashMap, + + /// Input types. + input_types: HashMap, } impl Default for ParsedGraphQLSchema { @@ -305,6 +308,7 @@ impl Default for ParsedGraphQLSchema { version: String::default(), internal_types: HashMap::new(), pagination_type_map: HashMap::new(), + input_types: HashMap::new(), } } } @@ -328,8 +332,9 @@ impl ParsedGraphQLSchema { if let Some(schema) = schema { // Parse _everything_ in the GraphQL schema let mut ast = parse_schema(schema.schema())?; + println!("{ast:#?}"); - ast = inject_internal_types_into_document(ast, &base_type_names); + ast = inject_internal_types_into_document(ast); decoder.decode_service_document(ast)?; @@ -410,6 +415,10 @@ impl ParsedGraphQLSchema { &self.pagination_type_map } + pub fn input_types(&self) -> &HashMap { + &self.input_types + } + /// Return the base scalar type for a given `FieldDefinition`. pub fn scalar_type_for(&self, f: &FieldDefinition) -> String { let typ_name = list_field_type_name(f);