Skip to content

Commit

Permalink
graphql integration almost done
Browse files Browse the repository at this point in the history
  • Loading branch information
Larkooo committed Nov 29, 2024
1 parent 277aa89 commit 94e6dc1
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 188 deletions.
4 changes: 2 additions & 2 deletions crates/torii/graphql/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ pub const METADATA_TABLE: &str = "metadata";

pub const ID_COLUMN: &str = "id";
pub const EVENT_ID_COLUMN: &str = "event_id";
pub const ENTITY_ID_COLUMN: &str = "entity_id";
pub const EVENT_MESSAGE_ID_COLUMN: &str = "event_message_id";
pub const ENTITY_ID_COLUMN: &str = "internal_entity_id";
pub const EVENT_MESSAGE_ID_COLUMN: &str = "internal_event_message_id";
pub const JSON_COLUMN: &str = "json";
pub const TRANSACTION_HASH_COLUMN: &str = "transaction_hash";

Expand Down
4 changes: 2 additions & 2 deletions crates/torii/graphql/src/object/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ pub fn connection_output(
.map(|row| {
let order_field = match order {
Some(order) => {
if is_external {
format!("external_{}", order.field)
if !is_external {
format!("internal_{}", order.field)
} else {
order.field.to_string()
}
Expand Down
150 changes: 17 additions & 133 deletions crates/torii/graphql/src/object/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ impl ResolvableObject for EntityObject {
}

fn subscriptions(&self) -> Option<Vec<SubscriptionField>> {
Some(vec![
SubscriptionField::new("entityUpdated", TypeRef::named_nn(self.type_name()), |ctx| {
Some(vec![SubscriptionField::new(
"entityUpdated",
TypeRef::named_nn(self.type_name()),
|ctx| {
SubscriptionFieldFuture::new(async move {
let id = match ctx.args.get("id") {
Some(id) => Some(id.string()?.to_string()),
Expand All @@ -85,9 +87,9 @@ impl ResolvableObject for EntityObject {
}
}))
})
})
.argument(InputValue::new("id", TypeRef::named(TypeRef::ID))),
])
},
)
.argument(InputValue::new("id", TypeRef::named(TypeRef::ID)))])
}
}

Expand Down Expand Up @@ -144,21 +146,16 @@ fn model_union_field() -> Field {
})?;
let type_mapping = build_type_mapping(&namespace, &schema);

// but the table name for the model data is the unhashed model name
let data: ValueMapping = match model_data_recursive_query(
&mut conn,
ENTITY_ID_COLUMN,
vec![get_tag(&namespace, &name)],
&entity_id,
&[],
&type_mapping,
false,
)
.await?
{
Value::Object(map) => map,
_ => unreachable!(),
};
// Get the table name
let table_name = get_tag(&namespace, &name);

// Fetch the row data
let query = format!("SELECT * FROM [{}] WHERE internal_entity_id = ?", table_name);
let row =
sqlx::query(&query).bind(&entity_id).fetch_one(&mut *conn).await?;

// Use value_mapping_from_row to handle nested structures
let data = value_mapping_from_row(&row, &type_mapping, true)?;

results.push(FieldValue::with_type(
FieldValue::owned_any(data),
Expand All @@ -173,116 +170,3 @@ fn model_union_field() -> Field {
})
})
}

// TODO: flatten query
#[async_recursion]
pub async fn model_data_recursive_query(
conn: &mut PoolConnection<Sqlite>,
entity_id_column: &str,
path_array: Vec<String>,
entity_id: &str,
indexes: &[i64],
type_mapping: &TypeMapping,
is_list: bool,
) -> sqlx::Result<Value> {
// For nested types, we need to remove prefix in path array
let namespace = format!("{}_", path_array[0]);
let table_name = &path_array.join("$").replace(&namespace, "");
let mut query =
format!("SELECT * FROM [{}] WHERE {entity_id_column} = '{}' ", table_name, entity_id);
for (column_idx, index) in indexes.iter().enumerate() {
query.push_str(&format!("AND idx_{} = {} ", column_idx, index));
}

let rows = sqlx::query(&query).fetch_all(conn.as_mut()).await?;
if rows.is_empty() {
return Ok(Value::List(vec![]));
}

let value_mapping: Value;
let mut nested_value_mappings = Vec::new();

for (idx, row) in rows.iter().enumerate() {
let mut nested_value_mapping = value_mapping_from_row(row, type_mapping, true)?;

for (field_name, type_data) in type_mapping {
if let TypeData::Nested((_, nested_mapping)) = type_data {
let mut nested_path = path_array.clone();
nested_path.push(field_name.to_string());

let nested_values = model_data_recursive_query(
conn,
entity_id_column,
nested_path,
entity_id,
&if is_list {
let mut indexes = indexes.to_vec();
indexes.push(idx as i64);
indexes
} else {
indexes.to_vec()
},
nested_mapping,
false,
)
.await?;

nested_value_mapping.insert(Name::new(field_name), nested_values);
} else if let TypeData::List(inner) = type_data {
let mut nested_path = path_array.clone();
nested_path.push(field_name.to_string());

let data = match model_data_recursive_query(
conn,
entity_id_column,
nested_path,
entity_id,
// this might need to be changed to support 2d+ arrays
&if is_list {
let mut indexes = indexes.to_vec();
indexes.push(idx as i64);
indexes
} else {
indexes.to_vec()
},
&IndexMap::from([(Name::new("data"), *inner.clone())]),
true,
)
.await?
{
// map our list which uses a data field as a place holder
// for all elements to get the elemnt directly
Value::List(data) => data
.iter()
.map(|v| match v {
Value::Object(map) => map.get(&Name::new("data")).unwrap().clone(),
ty => unreachable!(
"Expected Value::Object for list \"data\" field, got {:?}",
ty
),
})
.collect(),
Value::Object(map) => map.get(&Name::new("data")).unwrap().clone(),
ty => {
unreachable!(
"Expected Value::List or Value::Object for list, got {:?}",
ty
);
}
};

nested_value_mapping.insert(Name::new(field_name), data);
}
}

nested_value_mappings.push(Value::Object(nested_value_mapping));
}

if is_list {
value_mapping = Value::List(nested_value_mappings);
} else {
value_mapping = nested_value_mappings.pop().unwrap();
}

Ok(value_mapping)
}
38 changes: 17 additions & 21 deletions crates/torii/graphql/src/object/event_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use tokio_stream::StreamExt;
use torii_core::simple_broker::SimpleBroker;
use torii_core::types::EventMessage;

use super::entity::model_data_recursive_query;
use super::inputs::keys_input::keys_argument;
use super::{BasicObject, ResolvableObject, TypeMapping, ValueMapping};
use crate::constants::{
Expand All @@ -19,8 +18,9 @@ use crate::constants::{
};
use crate::mapping::ENTITY_TYPE_MAPPING;
use crate::object::{resolve_many, resolve_one};
use crate::query::build_type_mapping;
use crate::query::{build_type_mapping, value_mapping_from_row};
use crate::utils;

#[derive(Debug)]
pub struct EventMessageObject;

Expand Down Expand Up @@ -75,16 +75,13 @@ impl ResolvableObject for EventMessageObject {
Some(id) => Some(id.string()?.to_string()),
None => None,
};
// if id is None, then subscribe to all entities
// if id is Some, then subscribe to only the entity with that id
Ok(SimpleBroker::<EventMessage>::subscribe().filter_map(
move |entity: EventMessage| {
if id.is_none() || id == Some(entity.id.clone()) {
Some(Ok(Value::Object(EventMessageObject::value_mapping(
entity,
))))
} else {
// id != entity.id , then don't send anything, still listening
None
}
},
Expand Down Expand Up @@ -129,7 +126,6 @@ fn model_union_field() -> Field {

let entity_id = utils::extract::<String>(indexmap, "id")?;
// fetch name from the models table
// using the model id (hashed model name)
let model_ids: Vec<(String, String, String)> = sqlx::query_as(
"SELECT namespace, name, schema
FROM models
Expand All @@ -150,21 +146,21 @@ fn model_union_field() -> Field {
})?;
let type_mapping = build_type_mapping(&namespace, &schema);

// but the table name for the model data is the unhashed model name
let data: ValueMapping = match model_data_recursive_query(
&mut conn,
EVENT_MESSAGE_ID_COLUMN,
vec![get_tag(&namespace, &name)],
&entity_id,
&[],
&type_mapping,
false,
)
.await?
{
Value::Object(map) => map,
_ => unreachable!(),
};
// Get the table name
let table_name = get_tag(&namespace, &name);

// Fetch the row data
let query = format!(
"SELECT * FROM [{}] WHERE internal_event_message_id = ?",
table_name
);
let row = sqlx::query(&query)
.bind(&entity_id)
.fetch_one(&mut *conn)
.await?;

// Use value_mapping_from_row to handle nested structures
let data = value_mapping_from_row(&row, &type_mapping, true)?;

results.push(FieldValue::with_type(
FieldValue::owned_any(data),
Expand Down
4 changes: 2 additions & 2 deletions crates/torii/graphql/src/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ pub fn resolve_one(
let id: String =
extract::<String>(ctx.args.as_index_map(), &id_column.to_case(Case::Camel))?;
let data = fetch_single_row(&mut conn, &table_name, &id_column, &id).await?;
let model = value_mapping_from_row(&data, &type_mapping, false)?;
let model = value_mapping_from_row(&data, &type_mapping, true)?;
Ok(Some(Value::Object(model)))
})
})
Expand Down Expand Up @@ -310,7 +310,7 @@ pub fn resolve_many(
&order,
&id_column,
total_count,
false,
true,
page_info,
)?;

Expand Down
10 changes: 6 additions & 4 deletions crates/torii/graphql/src/object/model_data.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use async_graphql::dynamic::{Enum, Field, FieldFuture, InputObject, Object, TypeRef};
use async_graphql::Value;
use dojo_types::naming::get_tag;
use dojo_types::schema::Ty;
use sqlx::Pool;
use sqlx::Sqlite;

Expand All @@ -25,16 +26,17 @@ pub struct ModelDataObject {
pub plural_name: String,
pub type_name: String,
pub type_mapping: TypeMapping,
pub schema: Ty,
pub where_input: WhereInputObject,
pub order_input: OrderInputObject,
}

impl ModelDataObject {
pub fn new(name: String, type_name: String, type_mapping: TypeMapping) -> Self {
pub fn new(name: String, type_name: String, type_mapping: TypeMapping, schema: Ty) -> Self {
let where_input = WhereInputObject::new(type_name.as_str(), &type_mapping);
let order_input = OrderInputObject::new(type_name.as_str(), &type_mapping);
let plural_name = format!("{}Models", name);
Self { name, plural_name, type_name, type_mapping, where_input, order_input }
Self { name, plural_name, type_name, type_mapping, schema, where_input, order_input }
}
}

Expand Down Expand Up @@ -104,7 +106,7 @@ impl ResolvableObject for ModelDataObject {
let (data, page_info) = fetch_multiple_rows(
&mut conn,
&table_name,
EVENT_ID_COLUMN,
"internal_event_id",
&None,
&order,
&filters,
Expand All @@ -116,7 +118,7 @@ impl ResolvableObject for ModelDataObject {
&data,
&type_mapping,
&order,
EVENT_ID_COLUMN,
"internal_event_id",
total_count,
true,
page_info,
Expand Down
Loading

0 comments on commit 94e6dc1

Please sign in to comment.