Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

torii-client fetch remote if entities not synced #39

Merged
merged 2 commits into from
Oct 30, 2023
Merged
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
Binary file modified bun.lockb
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ set -ex

# This example requires to *not* create ES modules, therefore we pass the flag
# `--target no-modules`
wasm-pack build --out-dir ../wasm --release --features console-error-panic
wasm-pack build --out-dir ../pkg --release --features console-error-panic
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

use std::str::FromStr;

use dojo_types::schema::EntityModel;
use futures::StreamExt;
use starknet::core::types::FieldElement;
use starknet::core::utils::cairo_short_string_to_felt;
use types::ClientConfig;
use types::{ClientConfig, IEntityModel};
use wasm_bindgen::prelude::*;

mod types;
Expand All @@ -14,7 +15,6 @@ mod utils;
use utils::parse_ty_as_json_str;

type JsFieldElement = JsValue;
type JsEntityModel = JsValue;

#[wasm_bindgen]
extern "C" {
Expand All @@ -32,9 +32,9 @@ pub struct Client {

#[wasm_bindgen]
impl Client {
/// Retrieves the model value of an entity.
/// Retrieves the model value of an entity. Will fetch from remote if the requested entity is not one of the entities that are being synced.
#[wasm_bindgen(js_name = getModelValue)]
pub fn get_model_value(
pub async fn get_model_value(
&self,
model: &str,
keys: Vec<JsFieldElement>,
Expand All @@ -46,40 +46,47 @@ impl Client {
.into_iter()
.map(serde_wasm_bindgen::from_value::<FieldElement>)
.collect::<Result<Vec<FieldElement>, _>>()
.map_err(|err| {
JsValue::from_str(format!("failed to parse entity keys: {err}").as_str())
})?;
.map_err(|err| JsValue::from(format!("failed to parse entity keys: {err}")))?;

match self
.inner
.entity(&EntityModel {
keys,
model: model.to_string(),
})
.await
{
Ok(Some(ty)) => Ok(js_sys::JSON::parse(&parse_ty_as_json_str(&ty).to_string())?),
Ok(None) => Ok(JsValue::NULL),

match self.inner.entity(model, &keys) {
Some(ty) => Ok(serde_wasm_bindgen::to_value(&parse_ty_as_json_str(&ty))?),
None => Ok(JsValue::NULL),
Err(err) => Err(JsValue::from(format!("failed to get entity: {err}"))),
}
}

/// Register new entities to be synced.
#[wasm_bindgen(js_name = addEntitiesToSync)]
pub async fn add_entities_to_sync(&self, entities: Vec<JsEntityModel>) -> Result<(), JsValue> {
pub async fn add_entities_to_sync(&self, entities: Vec<IEntityModel>) -> Result<(), JsValue> {
log("adding entities to sync...");

#[cfg(feature = "console-error-panic")]
console_error_panic_hook::set_once();

let entities = entities
.into_iter()
.map(serde_wasm_bindgen::from_value::<dojo_types::schema::EntityModel>)
.map(|e| serde_wasm_bindgen::from_value(e.into()))
.collect::<Result<Vec<_>, _>>()?;

self.inner
.add_entities_to_sync(entities)
.await
.map_err(|err| JsValue::from_str(&err.to_string()))
.map_err(|err| JsValue::from(err.to_string()))
}

/// Remove the entities from being synced.
#[wasm_bindgen(js_name = removeEntitiesToSync)]
pub async fn remove_entities_to_sync(
&self,
entities: Vec<JsEntityModel>,
entities: Vec<IEntityModel>,
) -> Result<(), JsValue> {
log("removing entities to sync...");

Expand All @@ -88,28 +95,33 @@ impl Client {

let entities = entities
.into_iter()
.map(serde_wasm_bindgen::from_value::<dojo_types::schema::EntityModel>)
.map(|e| serde_wasm_bindgen::from_value(e.into()))
.collect::<Result<Vec<_>, _>>()?;

self.inner
.remove_entities_to_sync(entities)
.await
.map_err(|err| JsValue::from_str(&err.to_string()))
.map_err(|err| JsValue::from(err.to_string()))
}

/// Register a callback to be called every time the specified entity change.
#[wasm_bindgen(js_name = onEntityChange)]
pub fn on_entity_change(
/// Register a callback to be called every time the specified synced entity's value changes.
#[wasm_bindgen(js_name = onSyncEntityChange)]
pub fn on_sync_entity_change(
&self,
entity: JsEntityModel,
entity: IEntityModel,
callback: js_sys::Function,
) -> Result<(), JsValue> {
#[cfg(feature = "console-error-panic")]
console_error_panic_hook::set_once();

let entity = serde_wasm_bindgen::from_value::<dojo_types::schema::EntityModel>(entity)?;
let entity =
serde_wasm_bindgen::from_value::<dojo_types::schema::EntityModel>(entity.into())?;
let model = cairo_short_string_to_felt(&entity.model).expect("invalid model name");
let mut rcv = self.inner.storage().add_listener(model, &entity.keys).unwrap();
let mut rcv = self
.inner
.storage()
.add_listener(model, &entity.keys)
.unwrap();

wasm_bindgen_futures::spawn_local(async move {
while rcv.next().await.is_some() {
Expand All @@ -123,34 +135,38 @@ impl Client {

/// Create the a client with the given configurations.
#[wasm_bindgen(js_name = createClient)]
#[allow(non_snake_case)]
pub async fn create_client(
initial_entities_to_sync: Vec<JsEntityModel>,
initialEntitiesToSync: Vec<IEntityModel>,
config: ClientConfig,
) -> Result<Client, JsValue> {
#[cfg(feature = "console-error-panic")]
console_error_panic_hook::set_once();

let ClientConfig { rpc_url, torii_url, world_address } = config;
let ClientConfig {
rpc_url,
torii_url,
world_address,
} = config;

let entities = initial_entities_to_sync
let entities = initialEntitiesToSync
.into_iter()
.map(serde_wasm_bindgen::from_value::<dojo_types::schema::EntityModel>)
.map(|e| serde_wasm_bindgen::from_value(e.into()))
.collect::<Result<Vec<_>, _>>()?;

let world_address = FieldElement::from_str(&world_address).map_err(|err| {
JsValue::from_str(format!("failed to parse world address: {err}").as_str())
})?;
let world_address = FieldElement::from_str(&world_address)
.map_err(|err| JsValue::from(format!("failed to parse world address: {err}")))?;

let client = torii_client::client::ClientBuilder::new()
.set_entities_to_sync(entities)
.build(torii_url, rpc_url, world_address)
.await
.map_err(|err| JsValue::from_str(format!("failed to build client: {err}").as_str()))?;
.map_err(|err| JsValue::from(format!("failed to build client: {err}")))?;

wasm_bindgen_futures::spawn_local(client.start_subscription().await.map_err(|err| {
JsValue::from_str(
format!("failed to start torii client subscription service: {err}").as_str(),
)
JsValue::from(format!(
"failed to start torii client subscription service: {err}"
))
})?);

Ok(Client { inner: client })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
use serde::{Deserialize, Serialize};
use tsify::Tsify;
use wasm_bindgen::prelude::wasm_bindgen;

#[wasm_bindgen(typescript_custom_section)]
pub const ENTITY_MODEL_STR: &'static str = r#"
export interface EntityModel {
model: string;
keys: string[];
}
"#;

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "EntityModel")]
pub type IEntityModel;
}

#[derive(Tsify, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,28 @@ use dojo_types::primitive::Primitive;
use dojo_types::schema::Ty;
use serde_json::Value;

pub fn parse_ty_as_json_str(ty: &Ty) -> String {
fn parse_ty_as_json_str_impl(ty: &Ty) -> Value {
match ty {
Ty::Primitive(primitive) => primitive_value_json(*primitive),
pub fn parse_ty_as_json_str(ty: &Ty) -> Value {
match ty {
Ty::Primitive(primitive) => primitive_value_json(*primitive),

Ty::Struct(struct_ty) => struct_ty
.children
.iter()
.map(|child| (child.name.to_owned(), parse_ty_as_json_str_impl(&child.ty)))
.collect::<serde_json::Map<String, Value>>()
.into(),
Ty::Struct(struct_ty) => struct_ty
.children
.iter()
.map(|child| (child.name.to_owned(), parse_ty_as_json_str(&child.ty)))
.collect::<serde_json::Map<String, Value>>()
.into(),

Ty::Enum(enum_ty) => {
if let Some(option) = enum_ty.option {
let option = &enum_ty.options[option as usize];
Value::String(option.name.to_owned())
} else {
Value::Null
}
Ty::Enum(enum_ty) => {
if let Some(option) = enum_ty.option {
let option = &enum_ty.options[option as usize];
Value::String(option.name.to_owned())
} else {
Value::Null
}

Ty::Tuple(_) => unimplemented!("tuple not supported"),
}
}

parse_ty_as_json_str_impl(ty).to_string()
Ty::Tuple(_) => unimplemented!("tuple not supported"),
}
}

fn primitive_value_json(primitive: Primitive) -> Value {
Expand Down Expand Up @@ -84,8 +80,14 @@ mod test {
name: "PlayerKind".into(),
option: Some(1),
options: vec![
EnumOption { name: "Good".into(), ty: Ty::Tuple(vec![]) },
EnumOption { name: "Bad".into(), ty: Ty::Tuple(vec![]) },
EnumOption {
name: "Good".into(),
ty: Ty::Tuple(vec![]),
},
EnumOption {
name: "Bad".into(),
ty: Ty::Tuple(vec![]),
},
],
}),
},
Expand Down Expand Up @@ -148,8 +150,14 @@ mod test {
name: "PlayerKind".into(),
option: Some(1),
options: vec![
EnumOption { name: "Good".into(), ty: Ty::Tuple(vec![]) },
EnumOption { name: "Bad".into(), ty: Ty::Tuple(vec![]) },
EnumOption {
name: "Good".into(),
ty: Ty::Tuple(vec![]),
},
EnumOption {
name: "Bad".into(),
ty: Ty::Tuple(vec![]),
},
],
}),
},
Expand Down
16 changes: 13 additions & 3 deletions packages/torii-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "dist/index.js",
"type": "module",
"scripts": {
"build-wasm": "cd rust && ./build.sh",
"build-wasm": "cd crate && ./build.sh",
"build": "npm run build-wasm && tsc"
},
"exports": {
Expand All @@ -18,7 +18,17 @@
"author": "",
"license": "MIT",
"dependencies": {
"typescript": "^5.0.3",
"torii-client-wasm": "link:dojo-packages/packages/torii-client/wasm"
"starknet": "^5.19.5",
"torii-client-wasm": "link:dojo-packages/packages/torii-client/pkg",
"typescript": "^5.0.3"
},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/mocha": "^10.0.1",
"bun-types": "^1.0.1",
"fetch-mock": "^9.11.0",
"jest": "^29.5.0",
"ts-jest": "^29.1.0",
"tsup": "^7.2.0"
}
}
Loading