From a9c383179da99d4ea52c657ec1178a2f286b3f0e Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 17 Oct 2023 11:25:52 -0400 Subject: [PATCH] enhancement: transparently add internal types for GraphQL connection specification (#1403) * Implement types for connection specification * Ensure that internal fields are not used for struct construction * Make virtual entity check more specific * Don't create database artifacts for internal types * clippy fixes * Fix unit test * De-dupe redundant code * Ensure that column position calculated after filtering for internal types * Address ra0x3 feedback --- Cargo.lock | 1 + .../database-types/src/lib.rs | 26 +- packages/fuel-indexer-lib/Cargo.toml | 1 + packages/fuel-indexer-lib/src/graphql/mod.rs | 374 +++++++++++++++++- .../fuel-indexer-lib/src/graphql/parser.rs | 107 ++++- .../fuel-indexer-lib/src/graphql/validator.rs | 8 +- packages/fuel-indexer-macros/src/decoder.rs | 32 +- packages/fuel-indexer-macros/src/schema.rs | 4 + packages/fuel-indexer-schema/src/db/tables.rs | 4 +- 9 files changed, 507 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 257dba9ba..016a2b566 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3341,6 +3341,7 @@ version = "0.21.1" dependencies = [ "anyhow", "async-graphql-parser 5.0.10", + "async-graphql-value 5.0.10", "bincode", "clap 3.2.25", "fuel-indexer-types", diff --git a/packages/fuel-indexer-database/database-types/src/lib.rs b/packages/fuel-indexer-database/database-types/src/lib.rs index a872eb587..89f7912c7 100644 --- a/packages/fuel-indexer-database/database-types/src/lib.rs +++ b/packages/fuel-indexer-database/database-types/src/lib.rs @@ -14,7 +14,7 @@ use chrono::{ }; use fuel_indexer_lib::{ graphql::{ - extract_foreign_key_info, field_id, is_list_type, + check_for_directive, extract_foreign_key_info, field_id, is_list_type, types::{IdCol, ObjectCol}, JoinTableMeta, ParsedGraphQLSchema, }, @@ -305,10 +305,7 @@ impl Column { ..Self::default() }, false => { - let unique = f - .directives - .iter() - .any(|d| d.node.name.to_string() == "unique"); + let unique = check_for_directive(&f.directives, "unique"); Self { type_id, @@ -884,8 +881,9 @@ impl Table { let mut columns = o .fields .iter() + .filter(|f| !check_for_directive(&f.node.directives, "internal")) .enumerate() - .map(|(i, f)| { + .map(|(i, f)| Column::from_field_def( &f.node, parsed, @@ -893,7 +891,7 @@ impl Table { i as i32, persistence, ) - }) + ) .collect::>(); let mut constraints = Vec::new(); @@ -908,19 +906,11 @@ impl Table { return; } - let has_index = f - .node - .directives - .iter() - .any(|d| d.node.name.to_string() == "indexed" || d.node.name.to_string() == "unique"); + let has_index = check_for_directive(&f.node.directives, "indexed"); - let has_unique = f - .node - .directives - .iter() - .any(|d| d.node.name.to_string() == "unique"); + let has_unique = check_for_directive(&f.node.directives, "unique"); - if has_index { + if has_index || has_unique { constraints.push(Constraint::Index(SqlIndex { db_type: DbType::Postgres, table_name: typ.name.to_string().to_lowercase(), diff --git a/packages/fuel-indexer-lib/Cargo.toml b/packages/fuel-indexer-lib/Cargo.toml index 3c993320a..87b02a60e 100644 --- a/packages/fuel-indexer-lib/Cargo.toml +++ b/packages/fuel-indexer-lib/Cargo.toml @@ -12,6 +12,7 @@ description = "Fuel Indexer Library" [dependencies] anyhow = "1.0" async-graphql-parser = { workspace = true } +async-graphql-value = { workspace = true } bincode = { workspace = true } clap = { features = ["cargo", "derive", "env"], workspace = true } fuel-indexer-types = { workspace = true } diff --git a/packages/fuel-indexer-lib/src/graphql/mod.rs b/packages/fuel-indexer-lib/src/graphql/mod.rs index 21558cfe4..69bed9628 100644 --- a/packages/fuel-indexer-lib/src/graphql/mod.rs +++ b/packages/fuel-indexer-lib/src/graphql/mod.rs @@ -3,13 +3,20 @@ pub mod parser; pub mod types; pub mod validator; +use async_graphql_value::Name; pub use parser::{JoinTableMeta, ParsedError, ParsedGraphQLSchema}; pub use validator::GraphQLSchemaValidator; -use async_graphql_parser::types::FieldDefinition; +use async_graphql_parser::{ + types::{ + BaseType, ConstDirective, FieldDefinition, ObjectType, ServiceDocument, Type, + TypeDefinition, TypeKind, TypeSystemDefinition, + }, + Pos, Positioned, +}; use fuel_indexer_types::graphql::IndexMetadata; use sha2::{Digest, Sha256}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use types::IdCol; /// Maximum amount of foreign key list fields that can exist on a `TypeDefinition` @@ -32,6 +39,369 @@ fn inject_native_entities_into_schema(schema: &str) -> String { } } +/// 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 +/// the `ServiceDocument`. These types are not used to create database tables/columns +/// 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() { + 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![], + }, + )], + }, + ); + internal_fields.push(connection_field); + } + } + } + + obj.fields.append(&mut internal_fields); + } + } + } + + ast.definitions.append(&mut pagination_types); + + 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") + }; + + let edge_obj_type = ObjectType { + implements: vec![], + fields: vec![ + Positioned::position_node( + list_field, + FieldDefinition { + description: None, + name: Positioned::position_node(list_field, Name::new("node")), + arguments: vec![], + ty: Positioned::position_node( + list_field, + Type { + base: base_type.base.clone(), + nullable: false, + }, + ), + directives: vec![], + }, + ), + Positioned::position_node( + list_field, + FieldDefinition { + description: None, + name: Positioned::position_node(list_field, Name::new("cursor")), + arguments: vec![], + ty: Positioned::position_node( + list_field, + Type { + base: BaseType::Named(Name::new("String")), + nullable: false, + }, + ), + directives: vec![], + }, + ), + ], + }; + + TypeSystemDefinition::Type(Positioned::position_node( + list_field, + TypeDefinition { + extend: false, + description: None, + name: Positioned::position_node( + list_field, + Name::new(format!("{}Edge", name)), + ), + directives: vec![Positioned::position_node( + list_field, + ConstDirective { + name: Positioned::position_node(list_field, Name::new("internal")), + arguments: vec![], + }, + )], + kind: TypeKind::Object(edge_obj_type), + }, + )) +} + +/// Generate connection type defintion for a list field on an entity. +fn create_connection_type_def_for_list_entity(name: &Name) -> TypeSystemDefinition { + let dummy_position = Pos { + line: usize::MAX, + column: usize::MAX, + }; + + let obj_type = ObjectType { + implements: vec![], + fields: vec![ + Positioned::new( + FieldDefinition { + description: None, + name: Positioned::new(Name::new("nodes"), dummy_position), + arguments: vec![], + ty: Positioned::new( + Type { + base: BaseType::List(Box::new(Type { + base: BaseType::Named(name.clone()), + nullable: false, + })), + nullable: false, + }, + dummy_position, + ), + directives: vec![], + }, + dummy_position, + ), + Positioned::new( + FieldDefinition { + description: None, + name: Positioned::new(Name::new("edges"), dummy_position), + arguments: vec![], + ty: Positioned::new( + Type { + base: BaseType::List(Box::new(Type { + base: BaseType::Named(Name::new(format!( + "{}Edge", + name.clone() + ))), + nullable: false, + })), + nullable: false, + }, + dummy_position, + ), + directives: vec![], + }, + dummy_position, + ), + Positioned::new( + FieldDefinition { + description: None, + name: Positioned::new(Name::new("pageInfo"), dummy_position), + arguments: vec![], + ty: Positioned::new( + Type { + base: BaseType::Named(Name::new("PageInfo")), + nullable: false, + }, + dummy_position, + ), + directives: vec![], + }, + dummy_position, + ), + ], + }; + + TypeSystemDefinition::Type(Positioned::new( + TypeDefinition { + extend: false, + description: None, + name: Positioned::new( + Name::new(format!("{}Connection", name.clone())), + dummy_position, + ), + directives: vec![Positioned::new( + ConstDirective { + name: Positioned::new(Name::new("internal"), dummy_position), + arguments: vec![], + }, + dummy_position, + )], + kind: TypeKind::Object(obj_type), + }, + dummy_position, + )) +} + +/// Generate `PageInfo` type defintion for use in connection type defintions. +fn create_page_info_type_def() -> TypeSystemDefinition { + let dummy_position = Pos { + line: usize::MAX, + column: usize::MAX, + }; + + let obj_type = ObjectType { + implements: vec![], + fields: vec![ + Positioned::new( + FieldDefinition { + description: None, + name: Positioned::new(Name::new("hasPreviousPage"), dummy_position), + arguments: vec![], + ty: Positioned::new( + Type { + base: BaseType::Named(Name::new("Boolean")), + nullable: false, + }, + dummy_position, + ), + directives: vec![], + }, + dummy_position, + ), + Positioned::new( + FieldDefinition { + description: None, + name: Positioned::new(Name::new("hasNextPage"), dummy_position), + arguments: vec![], + ty: Positioned::new( + Type { + base: BaseType::Named(Name::new("Boolean")), + nullable: false, + }, + dummy_position, + ), + directives: vec![], + }, + dummy_position, + ), + Positioned::new( + FieldDefinition { + description: None, + name: Positioned::new(Name::new("startCursor"), dummy_position), + arguments: vec![], + ty: Positioned::new( + Type { + base: BaseType::Named(Name::new("String")), + nullable: true, + }, + dummy_position, + ), + directives: vec![], + }, + dummy_position, + ), + Positioned::new( + FieldDefinition { + description: None, + name: Positioned::new(Name::new("endCursor"), dummy_position), + arguments: vec![], + ty: Positioned::new( + Type { + base: BaseType::Named(Name::new("String")), + nullable: true, + }, + dummy_position, + ), + directives: vec![], + }, + dummy_position, + ), + Positioned::new( + FieldDefinition { + description: None, + name: Positioned::new(Name::new("totalCount"), dummy_position), + arguments: vec![], + ty: Positioned::new( + Type { + base: BaseType::Named(Name::new("U64")), + nullable: false, + }, + dummy_position, + ), + directives: vec![], + }, + dummy_position, + ), + ], + }; + + TypeSystemDefinition::Type(Positioned::new( + TypeDefinition { + extend: false, + description: None, + name: Positioned::new(Name::new("PageInfo"), dummy_position), + directives: vec![Positioned::new( + ConstDirective { + name: Positioned::new(Name::new("internal"), dummy_position), + arguments: vec![], + }, + dummy_position, + )], + kind: TypeKind::Object(obj_type), + }, + dummy_position, + )) +} + +pub fn check_for_directive( + directives: &[Positioned], + directive_name: &str, +) -> bool { + directives + .iter() + .any(|d| d.node.name.node == directive_name) +} + /// Wrapper for GraphQL schema content. #[derive(Default, Debug, Clone)] pub struct GraphQLSchema { diff --git a/packages/fuel-indexer-lib/src/graphql/parser.rs b/packages/fuel-indexer-lib/src/graphql/parser.rs index 0e609a7e7..4c1af66cd 100644 --- a/packages/fuel-indexer-lib/src/graphql/parser.rs +++ b/packages/fuel-indexer-lib/src/graphql/parser.rs @@ -6,8 +6,9 @@ use crate::{ fully_qualified_namespace, graphql::{ - extract_foreign_key_info, field_id, field_type_name, is_list_type, - list_field_type_name, GraphQLSchema, GraphQLSchemaValidator, IdCol, BASE_SCHEMA, + extract_foreign_key_info, field_id, field_type_name, + inject_internal_types_into_document, is_list_type, list_field_type_name, + GraphQLSchema, GraphQLSchemaValidator, IdCol, BASE_SCHEMA, }, join_table_name, ExecutionSource, }; @@ -18,10 +19,13 @@ use async_graphql_parser::{ TypeSystemDefinition, UnionType, }, }; +use async_graphql_value::ConstValue; use std::collections::{BTreeMap, HashMap, HashSet}; use thiserror::Error; +use super::check_for_directive; + /// Result type returned by parsing GraphQL schema. pub type ParsedResult = Result; @@ -261,6 +265,10 @@ pub struct ParsedGraphQLSchema { /// The version of the schema. version: String, + + /// Internal types. These types should not be added to a + /// database in any way; they are used to augment repsonses for introspection queries. + internal_types: HashSet, } impl Default for ParsedGraphQLSchema { @@ -289,6 +297,7 @@ impl Default for ParsedGraphQLSchema { join_table_meta: HashMap::new(), object_ordered_fields: HashMap::new(), version: String::default(), + internal_types: HashSet::new(), } } } @@ -301,11 +310,21 @@ impl ParsedGraphQLSchema { exec_source: ExecutionSource, schema: Option<&GraphQLSchema>, ) -> ParsedResult { + let base_type_names = { + let base_ast = parse_schema(BASE_SCHEMA)?; + let mut base_decoder = SchemaDecoder::new(); + base_decoder.decode_service_document(base_ast)?; + base_decoder.parsed_graphql_schema.type_names + }; + let mut decoder = SchemaDecoder::new(); if let Some(schema) = schema { // Parse _everything_ in the GraphQL schema - let ast = parse_schema(schema.schema())?; + let mut ast = parse_schema(schema.schema())?; + + ast = inject_internal_types_into_document(ast, &base_type_names); + decoder.decode_service_document(ast)?; decoder.parsed_graphql_schema.namespace = namespace.to_string(); @@ -316,13 +335,6 @@ impl ParsedGraphQLSchema { let mut result = decoder.get_parsed_schema(); - let base_type_names = { - let base_ast = parse_schema(BASE_SCHEMA)?; - let mut base_decoder = SchemaDecoder::new(); - base_decoder.decode_service_document(base_ast)?; - base_decoder.parsed_graphql_schema.type_names - }; - result.type_names.extend(base_type_names.clone()); result.scalar_names.extend(base_type_names); @@ -431,11 +443,15 @@ impl ParsedGraphQLSchema { self.unions.get(name) } - /// Return a list of all non-enum type definitions. - pub fn non_enum_typdefs(&self) -> Vec<(&String, &TypeDefinition)> { + /// Return a list of all type definitions that will have a record or table in + /// the database; functionally, this means any non-enum or internal type defintions. + pub fn storage_backed_typedefs(&self) -> Vec<(&String, &TypeDefinition)> { self.type_defs .iter() - .filter(|(_, t)| !matches!(&t.kind, TypeKind::Enum(_))) + .filter(|(_, t)| { + !matches!(&t.kind, TypeKind::Enum(_)) + && !self.is_internal_typedef(t.name.node.as_str()) + }) .collect() } @@ -445,6 +461,7 @@ impl ParsedGraphQLSchema { && !self.scalar_names.contains(name) && !self.is_enum_typedef(name) && !self.is_virtual_typedef(name) + && !self.is_internal_typedef(name) } /// Whether the given field type name is a type from which tables are not created. @@ -472,6 +489,10 @@ impl ParsedGraphQLSchema { self.union_names.contains(name) } + pub fn is_internal_typedef(&self, name: &str) -> bool { + self.internal_types.contains(name) + } + /// Return the GraphQL type for a given `FieldDefinition` name. fn field_type(&self, cond: &str, name: &str) -> Option<&String> { match self.object_field_mappings().get(cond) { @@ -693,6 +714,7 @@ impl SchemaDecoder { .parsed_graphql_schema .virtual_type_names .contains(&ftype) + && !self.parsed_graphql_schema.internal_types.contains(&ftype) { let (_ref_coltype, ref_colname, ref_tablename) = extract_foreign_key_info( @@ -764,13 +786,16 @@ impl SchemaDecoder { ) { GraphQLSchemaValidator::check_disallowed_graphql_typedef_name(&obj_name); - // Only parse `TypeDefinition`s with the `@entity` directive. - let is_entity = node - .directives - .iter() - .any(|d| d.node.name.to_string() == "entity"); + let is_internal = check_for_directive(&node.directives, "internal"); + let is_entity = check_for_directive(&node.directives, "entity"); - if !is_entity { + if is_internal { + self.parsed_graphql_schema + .internal_types + .insert(obj_name.clone()); + } + + if !is_entity && !is_internal { println!("Skipping TypeDefinition '{obj_name}', which is not marked with an @entity directive."); return; } @@ -785,7 +810,7 @@ impl SchemaDecoder { .directives .iter() .flat_map(|d| d.node.arguments.clone()) - .any(|t| t.0.node == "virtual"); + .any(|t| t.0.node == "virtual" && t.1.node == ConstValue::Boolean(true)); if is_virtual { self.parsed_graphql_schema @@ -841,6 +866,8 @@ impl SchemaDecoder { .parsed_graphql_schema .virtual_type_names .contains(&ftype) + && !self.parsed_graphql_schema.internal_types.contains(&ftype) + && !is_internal { GraphQLSchemaValidator::foreign_key_field_contains_no_unique_directive( &field.node, @@ -926,6 +953,7 @@ impl SchemaDecoder { .field_defs .insert(fid, (field.node.clone(), obj_name.clone())); } + self.parsed_graphql_schema .object_field_mappings .insert(obj_name, field_mapping); @@ -1044,7 +1072,44 @@ union Storage = Safe | Vault ); assert_eq!( parsed.join_table_meta().get("Storage").unwrap()[1], - JoinTableMeta::new("storage", "id", "user", "id", Some(3)) + JoinTableMeta::new("storage", "id", "user", "id", Some(4)) + ); + + // Internal types + assert!(parsed.internal_types.contains("AccountConnection")); + assert!(parsed.internal_types.contains("AccountEdge")); + assert!(parsed.internal_types.contains("UserConnection")); + assert!(parsed.internal_types.contains("UserEdge")); + } + + #[test] + fn test_internal_type_defs_in_object_field_mapping() { + let schema = r#" +type Foo @entity { + id: ID! + name: String! +} + +type Bar @entity { + id: ID! + foo: [Foo!]! +} +"#; + + let parsed = ParsedGraphQLSchema::new( + "test", + "test", + ExecutionSource::Wasm, + Some(&GraphQLSchema::new(schema.to_string())), + ); + + assert!(parsed.is_ok()); + + let parsed = parsed.unwrap(); + let bar_entity_fields = parsed.object_field_mappings.get("Bar").unwrap(); + assert_eq!( + bar_entity_fields.get("fooConnection").unwrap(), + &"FooConnection" ); } diff --git a/packages/fuel-indexer-lib/src/graphql/validator.rs b/packages/fuel-indexer-lib/src/graphql/validator.rs index 97eb81511..192c07bd2 100644 --- a/packages/fuel-indexer-lib/src/graphql/validator.rs +++ b/packages/fuel-indexer-lib/src/graphql/validator.rs @@ -4,6 +4,8 @@ use async_graphql_parser::types::{ }; use std::collections::HashSet; +use super::check_for_directive; + /// General container used to store a set of GraphQL schema validation functions. pub struct GraphQLSchemaValidator; @@ -108,10 +110,8 @@ impl GraphQLSchemaValidator { obj_name: &str, ) { let name = f.name.to_string(); - let has_unique_directive = f - .directives - .iter() - .any(|d| d.node.name.to_string() == "unique"); + + let has_unique_directive = check_for_directive(&f.directives, "unique"); if has_unique_directive { panic!("FieldDefinition({name}) on TypeDefinition({obj_name}) cannot contain a `@unique` directive."); } diff --git a/packages/fuel-indexer-macros/src/decoder.rs b/packages/fuel-indexer-macros/src/decoder.rs index cf50be7d9..6a1582b4f 100644 --- a/packages/fuel-indexer-macros/src/decoder.rs +++ b/packages/fuel-indexer-macros/src/decoder.rs @@ -5,7 +5,10 @@ use async_graphql_parser::types::{ use async_graphql_parser::{Pos, Positioned}; use async_graphql_value::Name; use fuel_indexer_lib::{ - graphql::{field_id, types::IdCol, ParsedGraphQLSchema, MAX_FOREIGN_KEY_LIST_FIELDS}, + graphql::{ + check_for_directive, field_id, types::IdCol, ParsedGraphQLSchema, + MAX_FOREIGN_KEY_LIST_FIELDS, + }, ExecutionSource, }; use fuel_indexer_types::type_id; @@ -74,6 +77,7 @@ impl Decoder for ImplementationDecoder { fn from_typedef(typ: &TypeDefinition, parsed: &ParsedGraphQLSchema) -> Self { match &typ.kind { TypeKind::Object(o) => { + // Remove any fields that use an internal type let obj_name = typ.name.to_string(); let mut struct_fields = quote! {}; @@ -89,6 +93,10 @@ impl Decoder for ImplementationDecoder { .collect::>(); for field in &o.fields { + if check_for_directive(&field.node.directives, "internal") { + continue; + } + let ProcessedTypedefField { field_name_ident, processed_type_result, @@ -176,7 +184,14 @@ impl Decoder for ImplementationDecoder { fields .iter() - .map(|f| f.0.name.to_string()) + // Remove any fields that are marked for internal use + .filter_map(|f| { + if check_for_directive(&f.0.directives, "internal") { + return None; + } + + Some(f.0.name.to_string()) + }) .collect::>() }) .filter_map(|field_name| { @@ -348,7 +363,14 @@ impl From for TokenStream { fields .iter() - .map(|f| f.0.name.to_string()) + // Remove any fields marked for internal use + .filter_map(|f| { + if check_for_directive(&f.0.directives, "internal") { + return None; + } + + Some(f.0.name.to_string()) + }) .collect::>() }) .filter_map(|field_name| { @@ -519,6 +541,10 @@ impl Decoder for ObjectDecoder { let mut fields_map = BTreeMap::new(); for field in o.fields.iter() { + if check_for_directive(&field.node.directives, "internal") { + continue; + } + let ProcessedTypedefField { field_name_ident, extractor, diff --git a/packages/fuel-indexer-macros/src/schema.rs b/packages/fuel-indexer-macros/src/schema.rs index 6192b3706..a50b752ae 100644 --- a/packages/fuel-indexer-macros/src/schema.rs +++ b/packages/fuel-indexer-macros/src/schema.rs @@ -15,6 +15,10 @@ fn process_type_def( parsed: &ParsedGraphQLSchema, typ: &TypeDefinition, ) -> Option { + if parsed.is_internal_typedef(typ.name.node.as_str()) { + return None; + } + let tokens = match &typ.kind { TypeKind::Object(_o) => ObjectDecoder::from_typedef(typ, parsed).into(), TypeKind::Enum(_e) => EnumDecoder::from_typedef(typ, parsed).into(), diff --git a/packages/fuel-indexer-schema/src/db/tables.rs b/packages/fuel-indexer-schema/src/db/tables.rs index b43cbe186..bc569a363 100644 --- a/packages/fuel-indexer-schema/src/db/tables.rs +++ b/packages/fuel-indexer-schema/src/db/tables.rs @@ -138,7 +138,7 @@ impl IndexerSchema { let mut tables = self .parsed - .non_enum_typdefs() + .storage_backed_typedefs() .iter() .map(|(_, t)| Table::from_typedef(t, &self.parsed)) .collect::>(); @@ -217,7 +217,7 @@ impl IndexerSchema { )?; let tables = parsed - .non_enum_typdefs() + .storage_backed_typedefs() .iter() .map(|(_, t)| Table::from_typedef(t, &parsed)) .collect::>();