Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 11 additions & 28 deletions crates/graphql/src/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,7 @@ pub static ENTITY_TYPE_MAPPING: LazyLock<TypeMapping> = LazyLock::new(|| {
Name::new("id"),
TypeData::Simple(TypeRef::named(TypeRef::ID)),
),
(
Name::new("keys"),
TypeData::Simple(TypeRef::named_list(TypeRef::STRING)),
),
(
Name::new("eventId"),
TypeData::Simple(TypeRef::named(TypeRef::STRING)),
),
// keys and eventId removed; align with storage Entity
(
Name::new("executedAt"),
TypeData::Simple(TypeRef::named(GraphqlType::DateTime.to_string())),
Expand Down Expand Up @@ -67,17 +60,11 @@ pub static EVENT_TYPE_MAPPING: LazyLock<TypeMapping> = LazyLock::new(|| {

pub static MODEL_TYPE_MAPPING: LazyLock<TypeMapping> = LazyLock::new(|| {
IndexMap::from([
(Name::new("namespace"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("name"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(
Name::new("id"),
TypeData::Simple(TypeRef::named(TypeRef::ID)),
),
(
Name::new("name"),
TypeData::Simple(TypeRef::named(TypeRef::STRING)),
),
(
Name::new("namespace"),
TypeData::Simple(TypeRef::named(TypeRef::STRING)),
Name::new("selector"),
TypeData::Simple(TypeRef::named(Primitive::Felt252(None).to_string())),
),
(
Name::new("classHash"),
Expand All @@ -87,18 +74,14 @@ pub static MODEL_TYPE_MAPPING: LazyLock<TypeMapping> = LazyLock::new(|| {
Name::new("contractAddress"),
TypeData::Simple(TypeRef::named(Primitive::Felt252(None).to_string())),
),
(Name::new("packedSize"), TypeData::Simple(TypeRef::named(TypeRef::INT))),
(Name::new("unpackedSize"), TypeData::Simple(TypeRef::named(TypeRef::INT))),
(
Name::new("transactionHash"),
TypeData::Simple(TypeRef::named(Primitive::Felt252(None).to_string())),
),
(
Name::new("executedAt"),
TypeData::Simple(TypeRef::named(GraphqlType::DateTime.to_string())),
),
(
Name::new("createdAt"),
TypeData::Simple(TypeRef::named(GraphqlType::DateTime.to_string())),
Name::new("useLegacyStore"),
TypeData::Simple(TypeRef::named(TypeRef::BOOLEAN)),
),
(Name::new("layout"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("schema"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
])
});

Expand Down
89 changes: 69 additions & 20 deletions crates/graphql/src/object/controller.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use async_graphql::dynamic::Field;
use async_graphql::dynamic::{Field, FieldFuture, InputValue, TypeRef};
use async_graphql::{Name, Value};
use starknet::core::types::Felt;
use std::str::FromStr;
use std::sync::Arc;
use torii_storage::{proto as storage_proto, ReadOnlyStorage};

use super::{BasicObject, ResolvableObject, TypeMapping};
use crate::constants::{CONTROLLER_NAMES, CONTROLLER_TABLE, CONTROLLER_TYPE_NAME, ID_COLUMN};
use crate::constants::{CONTROLLER_NAMES, CONTROLLER_TYPE_NAME};
use crate::mapping::CONTROLLER_MAPPING;
use crate::object::{resolve_many, resolve_one};
use crate::utils;

#[derive(Debug)]
pub struct ControllerObject;
Expand All @@ -24,22 +29,66 @@ impl BasicObject for ControllerObject {

impl ResolvableObject for ControllerObject {
fn resolvers(&self) -> Vec<Field> {
let resolve_one = resolve_one(
CONTROLLER_TABLE,
ID_COLUMN,
self.name().0,
self.type_name(),
self.type_mapping(),
);

let resolve_many = resolve_many(
CONTROLLER_TABLE,
ID_COLUMN,
self.name().1,
self.type_name(),
self.type_mapping(),
);

vec![resolve_one, resolve_many]
// Single controller by address
let get_one = Field::new(self.name().0, TypeRef::named_nn(self.type_name()), |ctx| {
FieldFuture::new(async move {
let storage = ctx.data::<Arc<dyn ReadOnlyStorage>>()?.clone();
let address: String = utils::extract::<String>(ctx.args.as_index_map(), "address")?;
let addr = Felt::from_str(&address).map_err(|e| anyhow::anyhow!(e.to_string()))?;
let query = storage_proto::ControllerQuery {
contract_addresses: vec![addr],
usernames: vec![],
pagination: storage_proto::Pagination { cursor: None, limit: Some(1), direction: storage_proto::PaginationDirection::Forward, order_by: vec![] },
};
let page = storage.controllers(&query).await.map_err(|e| anyhow::anyhow!(e.to_string()))?;
let item = page.items.into_iter().next().ok_or_else(|| anyhow::anyhow!("Controller not found"))?;
Ok(Some(Value::Object(value_from_controller(item))))
})
})
.argument(InputValue::new("address", TypeRef::named_nn(TypeRef::ID)));

// Many controllers with optional filters
let get_many = Field::new(self.name().1, TypeRef::named_list(self.type_name()), |ctx| {
FieldFuture::new(async move {
let storage = ctx.data::<Arc<dyn ReadOnlyStorage>>()?.clone();
let usernames = utils::extract::<Vec<String>>(ctx.args.as_index_map(), "usernames").unwrap_or_default();
let addresses = utils::extract::<Vec<String>>(ctx.args.as_index_map(), "contractAddresses").unwrap_or_default();
let limit = utils::extract::<u64>(ctx.args.as_index_map(), "limit").ok().map(|v| v as u32);
let cursor = utils::extract::<String>(ctx.args.as_index_map(), "cursor").ok();

let addrs: Vec<Felt> = addresses.into_iter().filter_map(|s| Felt::from_str(&s).ok()).collect();
let query = storage_proto::ControllerQuery {
contract_addresses: addrs,
usernames,
pagination: storage_proto::Pagination { cursor, limit, direction: storage_proto::PaginationDirection::Forward, order_by: vec![] },
};
let page = storage.controllers(&query).await.map_err(|e| anyhow::anyhow!(e.to_string()))?;
let list = page
.items
.into_iter()
.map(value_from_controller)
.map(Value::Object)
.collect::<Vec<_>>();
Ok(Some(Value::List(list)))
})
})
.argument(InputValue::new("usernames", TypeRef::named_list(TypeRef::STRING)))
.argument(InputValue::new("contractAddresses", TypeRef::named_list(TypeRef::ID)))
.argument(InputValue::new("limit", TypeRef::named(TypeRef::INT)))
.argument(InputValue::new("cursor", TypeRef::named(TypeRef::STRING)));

vec![get_one, get_many]
}
}

fn value_from_controller(c: storage_proto::Controller) -> async_graphql::dynamic::indexmap::IndexMap<Name, Value> {
async_graphql::dynamic::indexmap::IndexMap::from([
(Name::new("id"), Value::from(format!("{:#x}", c.address))),
(Name::new("username"), Value::from(c.username)),
(Name::new("address"), Value::from(format!("{:#x}", c.address))),
(
Name::new("deployedAt"),
Value::from(c.deployed_at.format(crate::constants::DATETIME_FORMAT).to_string()),
),
])
}
43 changes: 31 additions & 12 deletions crates/graphql/src/object/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ use torii_broker::types::{EventUpdate, InnerType};
use torii_broker::MemoryBroker;

use super::inputs::keys_input::{keys_argument, parse_keys_argument};
use super::{resolve_many, BasicObject, ResolvableObject, TypeMapping};
use crate::constants::{DATETIME_FORMAT, EVENT_NAMES, EVENT_TABLE, EVENT_TYPE_NAME, ID_COLUMN};
use super::{BasicObject, ResolvableObject, TypeMapping};
use crate::constants::{DATETIME_FORMAT, EVENT_NAMES, EVENT_TYPE_NAME};
use crate::mapping::EVENT_TYPE_MAPPING;
use crate::types::ValueMapping;
use std::sync::Arc;
use torii_storage::{proto as storage_proto, ReadOnlyStorage};
use async_graphql::dynamic::FieldFuture;

#[derive(Debug)]
pub struct EventObject;
Expand All @@ -34,16 +37,32 @@ impl BasicObject for EventObject {

impl ResolvableObject for EventObject {
fn resolvers(&self) -> Vec<Field> {
let mut resolve_many = resolve_many(
EVENT_TABLE,
ID_COLUMN,
self.name().1,
self.type_name(),
self.type_mapping(),
);
resolve_many = keys_argument(resolve_many);

vec![resolve_many]
// Events list via storage
let mut field = Field::new(self.name().1, TypeRef::named_list(self.type_name()), |ctx| {
FieldFuture::new(async move {
let storage = ctx.data::<Arc<dyn ReadOnlyStorage>>()?.clone();
// support optional keys filtering with KeysClause similar to subscription
let keys = parse_keys_argument(&ctx)?;
let pagination = storage_proto::Pagination { cursor: None, limit: None, direction: storage_proto::PaginationDirection::Forward, order_by: vec![] };
let query = storage_proto::EventQuery { keys: keys.map(|k| storage_proto::KeysClause { keys: k.iter().map(|s| Some(Felt::from_str(s).unwrap())).collect(), pattern_matching: storage_proto::PatternMatching::VariableLen, models: vec![] }), pagination };
let page = storage.events(query).await.map_err(|e| anyhow::anyhow!(e.to_string()))?;
let list = page.items.into_iter().map(|e| {
let keys: Vec<String> = e.keys.into_iter().map(|k| format!("{:#x}", k)).collect();
let data: Vec<String> = e.data.into_iter().map(|k| format!("{:#x}", k)).collect();
Value::Object(ValueMapping::from([
(Name::new("id"), Value::from("")),
(Name::new("keys"), Value::from(keys)),
(Name::new("data"), Value::from(data)),
(Name::new("transactionHash"), Value::from("")),
(Name::new("executedAt"), Value::from("")),
(Name::new("createdAt"), Value::from("")),
]))
}).collect::<Vec<_>>();
Ok(Some(Value::List(list)))
})
});
field = keys_argument(field);
vec![field]
}

fn subscriptions(&self) -> Option<Vec<SubscriptionField>> {
Expand Down
Loading
Loading