Skip to content

Commit

Permalink
Graphql event keys filtering (#1062)
Browse files Browse the repository at this point in the history
  • Loading branch information
broody authored Oct 20, 2023
1 parent 19d76ce commit 4ae61b3
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 42 deletions.
13 changes: 7 additions & 6 deletions crates/torii/graphql/src/object/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use torii_core::simple_broker::SimpleBroker;
use torii_core::types::Entity;

use super::connection::{connection_arguments, connection_output, parse_connection_arguments};
use super::inputs::keys_input::{keys_argument, parse_keys_argument};
use super::{ObjectTrait, TypeMapping, ValueMapping};
use crate::mapping::ENTITY_TYPE_MAPPING;
use crate::query::constants::ENTITY_TABLE;
use crate::query::constants::{ENTITY_TABLE, EVENT_ID_COLUMN};
use crate::query::data::{count_rows, fetch_multiple_rows};
use crate::query::{type_mapping_query, value_mapping_from_row};
use crate::types::TypeData;
Expand Down Expand Up @@ -71,12 +72,12 @@ impl ObjectTrait for EntityObject {
FieldFuture::new(async move {
let mut conn = ctx.data::<Pool<Sqlite>>()?.acquire().await?;
let connection = parse_connection_arguments(&ctx)?;
let keys = extract::<Vec<String>>(ctx.args.as_index_map(), "keys").ok();
let keys = parse_keys_argument(&ctx)?;
let total_count = count_rows(&mut conn, ENTITY_TABLE, &keys, &None).await?;
let data = fetch_multiple_rows(
&mut conn,
ENTITY_TABLE,
"event_id",
EVENT_ID_COLUMN,
&keys,
&None,
&None,
Expand All @@ -87,18 +88,18 @@ impl ObjectTrait for EntityObject {
&data,
&ENTITY_TYPE_MAPPING,
&None,
"event_id",
EVENT_ID_COLUMN,
total_count,
false,
)?;

Ok(Some(Value::Object(results)))
})
},
)
.argument(InputValue::new("keys", TypeRef::named_list(TypeRef::STRING)));
);

field = connection_arguments(field);
field = keys_argument(field);

Some(field)
}
Expand Down
46 changes: 44 additions & 2 deletions crates/torii/graphql/src/object/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ use async_graphql::dynamic::{Field, FieldFuture, TypeRef};
use async_graphql::Value;
use sqlx::{Pool, Sqlite};

use super::connection::{connection_arguments, connection_output, parse_connection_arguments};
use super::inputs::keys_input::{keys_argument, parse_keys_argument};
use super::{ObjectTrait, TypeMapping, ValueMapping};
use crate::mapping::{EVENT_TYPE_MAPPING, SYSTEM_CALL_TYPE_MAPPING};
use crate::query::constants::EVENT_TABLE;
use crate::query::data::fetch_single_row;
use crate::query::constants::{EVENT_TABLE, ID_COLUMN};
use crate::query::data::{count_rows, fetch_multiple_rows, fetch_single_row};
use crate::query::value_mapping_from_row;
use crate::utils::extract;

Expand All @@ -32,6 +34,46 @@ impl ObjectTrait for EventObject {
None
}

fn resolve_many(&self) -> Option<Field> {
let mut field = Field::new(
self.name().1,
TypeRef::named(format!("{}Connection", self.type_name())),
|ctx| {
FieldFuture::new(async move {
let mut conn = ctx.data::<Pool<Sqlite>>()?.acquire().await?;
let connection = parse_connection_arguments(&ctx)?;
let keys = parse_keys_argument(&ctx)?;
let total_count = count_rows(&mut conn, EVENT_TABLE, &keys, &None).await?;
let data = fetch_multiple_rows(
&mut conn,
EVENT_TABLE,
ID_COLUMN,
&keys,
&None,
&None,
&connection,
)
.await?;
let results = connection_output(
&data,
&EVENT_TYPE_MAPPING,
&None,
ID_COLUMN,
total_count,
false,
)?;

Ok(Some(Value::Object(results)))
})
},
);

field = connection_arguments(field);
field = keys_argument(field);

Some(field)
}

fn related_fields(&self) -> Option<Vec<Field>> {
Some(vec![Field::new("systemCall", TypeRef::named_nn("SystemCall"), |ctx| {
FieldFuture::new(async move {
Expand Down
31 changes: 31 additions & 0 deletions crates/torii/graphql/src/object/inputs/keys_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use async_graphql::dynamic::{Field, InputValue, ResolverContext, TypeRef};
use async_graphql::Error;

use crate::utils::extract;

pub fn keys_argument(field: Field) -> Field {
field.argument(InputValue::new("keys", TypeRef::named_list(TypeRef::STRING)))
}

pub fn parse_keys_argument(ctx: &ResolverContext<'_>) -> Result<Option<Vec<String>>, Error> {
let keys = extract::<Vec<String>>(ctx.args.as_index_map(), "keys");

if let Ok(keys) = keys {
if !keys.iter().all(|s| is_hex_or_star(s)) {
return Err("Key parts can only be hex string or wild card `*`".into());
}

return Ok(Some(keys));
}

Ok(None)
}

fn is_hex_or_star(s: &str) -> bool {
if s == "*" {
return true;
}
let s = if let Some(stripped) = s.strip_prefix("0x") { stripped } else { s };

s.chars().all(|c| c.is_ascii_hexdigit())
}
1 change: 1 addition & 0 deletions crates/torii/graphql/src/object/inputs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use async_graphql::dynamic::{Enum, InputObject};

use super::TypeMapping;

pub mod keys_input;
pub mod order_input;
pub mod where_input;

Expand Down
1 change: 1 addition & 0 deletions crates/torii/graphql/src/query/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub const SYSTEM_TABLE: &str = "systems";
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 INTERNAL_ENTITY_ID_KEY: &str = "$entity_id$";
50 changes: 17 additions & 33 deletions crates/torii/graphql/src/query/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,7 @@ pub async fn count_rows(
filters: &Option<Vec<Filter>>,
) -> Result<i64> {
let mut query = format!("SELECT COUNT(*) FROM {}", table_name);
let mut conditions = Vec::new();

if let Some(keys) = keys {
let keys_str = keys.join("/");
conditions.push(format!("keys LIKE '{}/%'", keys_str));
}

if let Some(filters) = filters {
for filter in filters {
let condition = match filter.value {
FilterValue::Int(i) => format!("{} {} {}", filter.field, filter.comparator, i),
FilterValue::String(ref s) => {
format!("{} {} '{}'", filter.field, filter.comparator, s)
}
};

conditions.push(condition);
}
}
let conditions = build_conditions(keys, filters);

if !conditions.is_empty() {
query.push_str(&format!(" WHERE {}", conditions.join(" AND ")));
Expand Down Expand Up @@ -61,12 +43,7 @@ pub async fn fetch_multiple_rows(
filters: &Option<Vec<Filter>>,
connection: &ConnectionArguments,
) -> Result<Vec<SqliteRow>> {
let mut conditions = Vec::new();

if let Some(keys) = &keys {
let keys_str = keys.join("/");
conditions.push(format!("keys LIKE '{}/%'", keys_str));
}
let mut conditions = build_conditions(keys, filters);

if let Some(after_cursor) = &connection.after {
conditions.push(handle_cursor(after_cursor, order, CursorDirection::After, id_column)?);
Expand All @@ -76,10 +53,6 @@ pub async fn fetch_multiple_rows(
conditions.push(handle_cursor(before_cursor, order, CursorDirection::Before, id_column)?);
}

if let Some(filters) = filters {
conditions.extend(filters.iter().map(handle_filter));
}

let mut query = format!("SELECT * FROM {}", table_name);
if !conditions.is_empty() {
query.push_str(&format!(" WHERE {}", conditions.join(" AND ")));
Expand Down Expand Up @@ -147,9 +120,20 @@ fn handle_cursor(
}
}

fn handle_filter(filter: &Filter) -> String {
match &filter.value {
FilterValue::Int(i) => format!("{} {} {}", filter.field, filter.comparator, i),
FilterValue::String(s) => format!("{} {} '{}'", filter.field, filter.comparator, s),
fn build_conditions(keys: &Option<Vec<String>>, filters: &Option<Vec<Filter>>) -> Vec<String> {
let mut conditions = Vec::new();

if let Some(keys) = &keys {
let keys_str = keys.join("/").replace('*', "%");
conditions.push(format!("keys LIKE '{}/%'", keys_str));
}

if let Some(filters) = filters {
conditions.extend(filters.iter().map(|filter| match &filter.value {
FilterValue::Int(i) => format!("{} {} {}", filter.field, filter.comparator, i),
FilterValue::String(s) => format!("{} {} '{}'", filter.field, filter.comparator, s),
}));
}

conditions
}
17 changes: 16 additions & 1 deletion crates/torii/graphql/src/tests/types-test/src/systems.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,25 @@ mod records {
use types_test::{seed, random};
use super::IRecords;

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
RecordLogged: RecordLogged
}

#[derive(Drop, starknet::Event)]
struct RecordLogged {
record_id: u32,
type_u8: u8,
type_felt: felt252,
}

#[external(v0)]
impl RecordsImpl of IRecords<ContractState> {
fn create(self: @ContractState, num_records: u8) {
let world = self.world_dispatcher.read();
let mut record_idx = 0;

loop {
if record_idx == num_records {
break ();
Expand Down Expand Up @@ -80,6 +93,8 @@ mod records {
);

record_idx += 1;

emit!(world, RecordLogged { record_id, type_u8: record_idx.into(), type_felt });
};
return ();
}
Expand Down

0 comments on commit 4ae61b3

Please sign in to comment.