use crate::db::IndexerSchemaDbResult;
use fuel_indexer_database::{
queries, types::*, DbType, IndexerConnection, IndexerConnectionPool,
};
use fuel_indexer_lib::graphql::{GraphQLSchema, ParsedGraphQLSchema};
use fuel_indexer_lib::manifest::Manifest;
use itertools::Itertools;
#[derive(Default)]
pub struct IndexerSchema {
db_type: DbType,
parsed: ParsedGraphQLSchema,
schema: GraphQLSchema,
tables: Vec<Table>,
namespace: String,
identifier: String,
}
impl IndexerSchema {
pub fn new(
namespace: &str,
identifier: &str,
schema: &GraphQLSchema,
db_type: DbType,
) -> IndexerSchemaDbResult<Self> {
Ok(IndexerSchema {
db_type,
namespace: namespace.to_string(),
identifier: identifier.to_string(),
schema: schema.to_owned(),
parsed: ParsedGraphQLSchema::new(namespace, identifier, Some(schema))?,
tables: Vec::new(),
})
}
pub fn parsed(&self) -> &ParsedGraphQLSchema {
&self.parsed
}
pub async fn commit(
mut self,
schema: &GraphQLSchema,
conn: &mut IndexerConnection,
) -> IndexerSchemaDbResult<Self> {
let parsed_schema =
ParsedGraphQLSchema::new(&self.namespace, &self.identifier, Some(schema))?;
let mut statements = Vec::new();
self.schema = schema.to_owned();
self.parsed = parsed_schema;
let root = GraphRoot {
version: schema.version().to_owned(),
schema_name: self.namespace.to_owned(),
schema_identifier: self.identifier.to_owned(),
schema: self.schema.to_string(),
..GraphRoot::default()
};
queries::new_graph_root(conn, root).await?;
match self.db_type {
DbType::Postgres => {
let create = format!(
"CREATE SCHEMA IF NOT EXISTS {};",
self.parsed.fully_qualified_namespace(),
);
statements.push(create);
}
}
let mut type_ids = self
.parsed
.type_defs()
.iter()
.map(|(_, t)| TypeId::from_typedef(t, &self.parsed))
.unique_by(|t| t.id)
.collect::<Vec<TypeId>>();
let mut join_type_ids = self
.parsed
.join_table_meta()
.iter()
.flat_map(|(_, meta)| {
meta.iter()
.map(|m| TypeId::from_join_meta(m.to_owned(), &self.parsed))
})
.collect::<Vec<TypeId>>();
type_ids.append(&mut join_type_ids);
queries::type_id_insert(conn, type_ids).await?;
let mut tables = self
.parsed
.storage_backed_typedefs()
.iter()
.map(|(_, t)| Table::from_typedef(t, &self.parsed))
.collect::<Vec<Table>>();
let mut join_tables = self
.parsed
.join_table_meta()
.iter()
.flat_map(|(_, meta)| {
meta.iter()
.map(|m| Table::from_join_meta(m.to_owned(), &self.parsed))
})
.collect::<Vec<Table>>();
tables.append(&mut join_tables);
let columns = tables
.iter()
.flat_map(|t| t.columns())
.map(|c| c.to_owned())
.collect::<Vec<Column>>();
queries::new_column_insert(conn, columns).await?;
let table_stmnts = tables
.iter()
.filter_map(|t| {
let stmnt = t.create();
if stmnt.is_empty() {
return None;
}
Some(stmnt)
})
.collect::<Vec<String>>();
statements.extend(table_stmnts);
let constraint_stmnts = tables
.iter()
.flat_map(|t| t.constraints())
.map(|c| c.create())
.collect::<Vec<String>>();
statements.extend(constraint_stmnts);
for stmnt in statements.iter() {
queries::execute_query(conn, stmnt.to_owned()).await?;
}
self.tables = tables;
Ok(self)
}
pub async fn load(
pool: &IndexerConnectionPool,
namespace: &str,
identifier: &str,
) -> IndexerSchemaDbResult<Self> {
let mut conn = pool.acquire().await?;
let root = queries::graph_root_latest(&mut conn, namespace, identifier).await?;
let indexer_id =
queries::get_indexer_id(&mut conn, namespace, identifier).await?;
let IndexerAsset { bytes, .. } =
queries::indexer_asset(&mut conn, &indexer_id, IndexerAssetType::Manifest)
.await?;
let _manifest = Manifest::try_from(&bytes)?;
let schema = GraphQLSchema::new(root.schema.clone());
let parsed = ParsedGraphQLSchema::new(namespace, identifier, Some(&schema))?;
let tables = parsed
.storage_backed_typedefs()
.iter()
.map(|(_, t)| Table::from_typedef(t, &parsed))
.collect::<Vec<Table>>();
Ok(IndexerSchema {
namespace: root.schema_name,
identifier: root.schema_identifier,
schema,
tables,
parsed,
db_type: DbType::Postgres,
})
}
}