Skip to content

Commit

Permalink
Torii metadata query format
Browse files Browse the repository at this point in the history
  • Loading branch information
broody committed Nov 1, 2023
1 parent 74f5a72 commit 18256bd
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 28 deletions.
5 changes: 5 additions & 0 deletions crates/torii/graphql/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ 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 JSON_COLUMN: &str = "json";

pub const INTERNAL_ENTITY_ID_KEY: &str = "$entity_id$";

// objects namespaced to avoid conflicts with user models
pub const ENTITY_TYPE_NAME: &str = "World__Entity";
pub const MODEL_TYPE_NAME: &str = "World__Model";
pub const EVENT_TYPE_NAME: &str = "World__Event";
pub const SOCIAL_TYPE_NAME: &str = "World__Social";
pub const CONTENT_TYPE_NAME: &str = "World__Content";
pub const METADATA_TYPE_NAME: &str = "World__Metadata";
pub const TRANSACTION_TYPE_NAME: &str = "World__Transaction";
pub const QUERY_TYPE_NAME: &str = "World__Query";
Expand All @@ -26,5 +29,7 @@ pub const SUBSCRIPTION_TYPE_NAME: &str = "World__Subscription";
pub const ENTITY_NAMES: (&str, &str) = ("entity", "entities");
pub const MODEL_NAMES: (&str, &str) = ("model", "models");
pub const EVENT_NAMES: (&str, &str) = ("event", "events");
pub const SOCIAL_NAMES: (&str, &str) = ("social", "socials");
pub const CONTENT_NAMES: (&str, &str) = ("content", "contents");
pub const METADATA_NAMES: (&str, &str) = ("metadata", "metadatas");
pub const TRANSACTION_NAMES: (&str, &str) = ("transaction", "transactions");
18 changes: 17 additions & 1 deletion crates/torii/graphql/src/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use async_graphql::Name;
use dojo_types::primitive::Primitive;
use lazy_static::lazy_static;

use crate::constants::{CONTENT_TYPE_NAME, SOCIAL_TYPE_NAME};
use crate::types::{GraphqlType, TypeData, TypeMapping};

lazy_static! {
Expand Down Expand Up @@ -90,10 +91,25 @@ lazy_static! {
TypeData::Simple(TypeRef::named(GraphqlType::Cursor.to_string())),
),
]);
pub static ref SOCIAL_TYPE_MAPPING: TypeMapping = IndexMap::from([
(Name::new("name"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("url"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
]);
pub static ref CONTENT_TYPE_MAPPING: TypeMapping = IndexMap::from([
(Name::new("name"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("description"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("website"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("icon_uri"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("cover_uri"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("socials"), TypeData::Simple(TypeRef::named_list(SOCIAL_TYPE_NAME)))
]);
pub static ref METADATA_TYPE_MAPPING: TypeMapping = IndexMap::from([
(Name::new("id"), TypeData::Simple(TypeRef::named(TypeRef::ID))),
(Name::new("uri"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("json"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(
Name::new("content"),
TypeData::Nested((TypeRef::named(CONTENT_TYPE_NAME), IndexMap::new()))
),
(Name::new("icon_img"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(Name::new("cover_img"), TypeData::Simple(TypeRef::named(TypeRef::STRING))),
(
Expand Down
23 changes: 0 additions & 23 deletions crates/torii/graphql/src/object/metadata.rs

This file was deleted.

29 changes: 29 additions & 0 deletions crates/torii/graphql/src/object/metadata/content.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use async_graphql::dynamic::Field;

use super::{ObjectTrait, TypeMapping};
use crate::constants::{CONTENT_NAMES, CONTENT_TYPE_NAME};
use crate::mapping::CONTENT_TYPE_MAPPING;

pub struct ContentObject;

impl ObjectTrait for ContentObject {
fn name(&self) -> (&str, &str) {
CONTENT_NAMES
}

fn type_name(&self) -> &str {
CONTENT_TYPE_NAME
}

fn type_mapping(&self) -> &TypeMapping {
&CONTENT_TYPE_MAPPING
}

fn resolve_one(&self) -> Option<Field> {
None
}

fn resolve_many(&self) -> Option<Field> {
None
}
}
149 changes: 149 additions & 0 deletions crates/torii/graphql/src/object/metadata/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use async_graphql::dynamic::{Field, FieldFuture, TypeRef};
use async_graphql::{Name, Value};
use sqlx::sqlite::SqliteRow;
use sqlx::{Pool, Row, Sqlite};

use super::connection::{connection_arguments, cursor, parse_connection_arguments};
use super::ObjectTrait;
use crate::constants::{
ID_COLUMN, JSON_COLUMN, METADATA_NAMES, METADATA_TABLE, METADATA_TYPE_NAME,
};
use crate::mapping::METADATA_TYPE_MAPPING;
use crate::query::data::{count_rows, fetch_multiple_rows};
use crate::query::value_mapping_from_row;
use crate::types::{TypeMapping, ValueMapping};

pub mod content;
pub mod social;

pub struct MetadataObject;

impl ObjectTrait for MetadataObject {
fn name(&self) -> (&str, &str) {
METADATA_NAMES
}

fn type_name(&self) -> &str {
METADATA_TYPE_NAME
}

fn type_mapping(&self) -> &TypeMapping {
&METADATA_TYPE_MAPPING
}

fn table_name(&self) -> Option<&str> {
Some(METADATA_TABLE)
}

fn resolve_one(&self) -> Option<Field> {
None
}

fn resolve_many(&self) -> Option<Field> {
let type_mapping = self.type_mapping().clone();
let table_name = self.table_name().unwrap().to_string();

let mut field = Field::new(
self.name().1,
TypeRef::named(format!("{}Connection", self.type_name())),
move |ctx| {
let type_mapping = type_mapping.clone();
let table_name = table_name.to_string();

FieldFuture::new(async move {
let mut conn = ctx.data::<Pool<Sqlite>>()?.acquire().await?;
let connection = parse_connection_arguments(&ctx)?;
let total_count = count_rows(&mut conn, &table_name, &None, &None).await?;
let data = fetch_multiple_rows(
&mut conn,
&table_name,
ID_COLUMN,
&None,
&None,
&None,
&connection,
)
.await?;

// convert json field to value_mapping expected by content object
let results = metadata_connection_output(&data, &type_mapping, total_count)?;

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

field = connection_arguments(field);

Some(field)
}
}

// NOTE: need to generalize `connection_output` or maybe preprocess to support both predefined
// objects AND dynamic model objects
fn metadata_connection_output(
data: &[SqliteRow],
types: &TypeMapping,
total_count: i64,
) -> sqlx::Result<ValueMapping> {
let edges = data
.iter()
.map(|row| {
let order = row.try_get::<String, &str>(ID_COLUMN)?;
let cursor = cursor::encode(&order, &order);
let mut value_mapping = value_mapping_from_row(row, types, false)?;

let json_str = row.try_get::<String, &str>(JSON_COLUMN)?;
let serde_value: serde_json::Value =
serde_json::from_str(&json_str).map_err(|e| sqlx::Error::Decode(e.into()))?;

let content = ValueMapping::from([
extract_str_mapping("name", &serde_value),
extract_str_mapping("description", &serde_value),
extract_str_mapping("website", &serde_value),
extract_str_mapping("icon_uri", &serde_value),
extract_str_mapping("cover_uri", &serde_value),
extract_socials_mapping("socials", &serde_value),
]);

value_mapping.insert(Name::new("content"), Value::Object(content));

let mut edge = ValueMapping::new();
edge.insert(Name::new("node"), Value::Object(value_mapping));
edge.insert(Name::new("cursor"), Value::String(cursor));

Ok(Value::Object(edge))
})
.collect::<sqlx::Result<Vec<Value>>>();

Ok(ValueMapping::from([
(Name::new("total_count"), Value::from(total_count)),
(Name::new("edges"), Value::List(edges?)),
]))
}

fn extract_str_mapping(name: &str, serde_value: &serde_json::Value) -> (Name, Value) {
if let Some(serde_json::Value::String(str)) = serde_value.get(name) {
return (Name::new(name), Value::String(str.to_owned()));
}

(Name::new(name), Value::Null)
}

fn extract_socials_mapping(name: &str, serde_value: &serde_json::Value) -> (Name, Value) {
if let Some(serde_json::Value::Object(obj)) = serde_value.get(name) {
let list = obj
.iter()
.map(|(social_name, social_url)| {
Value::Object(ValueMapping::from([
(Name::new("name"), Value::String(social_name.to_string())),
(Name::new("url"), Value::String(social_url.as_str().unwrap().to_string())),
]))
})
.collect::<Vec<Value>>();

return (Name::new(name), Value::List(list));
}

(Name::new(name), Value::List(vec![]))
}
29 changes: 29 additions & 0 deletions crates/torii/graphql/src/object/metadata/social.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use async_graphql::dynamic::Field;

use super::{ObjectTrait, TypeMapping};
use crate::constants::{SOCIAL_NAMES, SOCIAL_TYPE_NAME};
use crate::mapping::SOCIAL_TYPE_MAPPING;

pub struct SocialObject;

impl ObjectTrait for SocialObject {
fn name(&self) -> (&str, &str) {
SOCIAL_NAMES
}

fn type_name(&self) -> &str {
SOCIAL_TYPE_NAME
}

fn type_mapping(&self) -> &TypeMapping {
&SOCIAL_TYPE_MAPPING
}

fn resolve_one(&self) -> Option<Field> {
None
}

fn resolve_many(&self) -> Option<Field> {
None
}
}
4 changes: 0 additions & 4 deletions crates/torii/graphql/src/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,6 @@ pub trait ObjectTrait: Send + Sync {
let mut object = Object::new(self.type_name());

for (field_name, type_data) in self.type_mapping().clone() {
if type_data.is_nested() {
continue;
}

let field = Field::new(field_name.to_string(), type_data.type_ref(), move |ctx| {
let field_name = field_name.clone();

Expand Down
4 changes: 4 additions & 0 deletions crates/torii/graphql/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use super::object::model_data::ModelDataObject;
use super::object::ObjectTrait;
use super::types::ScalarType;
use crate::constants::{QUERY_TYPE_NAME, SUBSCRIPTION_TYPE_NAME};
use crate::object::metadata::content::ContentObject;
use crate::object::metadata::social::SocialObject;
use crate::object::metadata::MetadataObject;
use crate::object::model::ModelObject;
use crate::object::transaction::TransactionObject;
Expand Down Expand Up @@ -108,6 +110,8 @@ async fn build_objects(pool: &SqlitePool) -> Result<(Vec<Box<dyn ObjectTrait>>,
let mut objects: Vec<Box<dyn ObjectTrait>> = vec![
Box::new(EntityObject),
Box::new(EventObject),
Box::new(SocialObject),
Box::new(ContentObject),
Box::new(MetadataObject),
Box::new(ModelObject),
Box::new(PageInfoObject),
Expand Down

0 comments on commit 18256bd

Please sign in to comment.