This repository has been archived by the owner on Oct 25, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add library with storage integration to replace
graphql-lib
(#1309)
- Loading branch information
Showing
66 changed files
with
33,930 additions
and
1,483 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 15 additions & 8 deletions
23
packages/fuel-indexer-graphql-lib/Cargo.toml → packages/fuel-indexer-graphql-dyn/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,40 @@ | ||
[package] | ||
name = "fuel-indexer-graphql-lib" | ||
name = "fuel-indexer-graphql-dyn" | ||
version = { workspace = true } | ||
authors = { workspace = true } | ||
edition = { workspace = true } | ||
homepage = { workspace = true } | ||
license = { workspace = true } | ||
repository = { workspace = true } | ||
rust-version = { workspace = true } | ||
description = "Fuel Indexer GraphQL Library" | ||
description = "Library for building executable dynamic GraphQL schemas" | ||
|
||
[features] | ||
default = ["json"] | ||
default = ["json", "testing"] | ||
json = ["dep:serde_json"] | ||
testing = ["dep:graphql-parser"] | ||
|
||
[dependencies] | ||
async-graphql = { version = "5.0", features = ["dynamic-schema", "dataloader"] } | ||
async-graphql-value = "6.0.3" | ||
async-graphql = { workspace = true, features = ["dynamic-schema", "dataloader"] } | ||
async-graphql-value = { workspace = true } | ||
async-trait = "0.1.73" | ||
extension-trait = "1.0.2" | ||
graphql-parser = { version = "0.4.0", optional = true } | ||
indexmap = "2.0.0" | ||
serde_json = { workspace = true, optional = true } | ||
strum = { version = "0.25.0", features = ["derive"] } | ||
tokio = { workspace = true, features = ["sync"] } | ||
thiserror = { workspace = true } | ||
anyhow = "1.0" | ||
lazy_static = "1.4.0" | ||
convert_case = "0.6.0" | ||
async-graphql-parser = "6.0.4" | ||
serde.workspace = true | ||
|
||
[dev-dependencies] | ||
assert_matches = "1.5.0" | ||
async-graphql-parser = "5.0" | ||
async-graphql-parser = { workspace = true } | ||
graphql-parser = "0.4.0" | ||
insta = { version = "1.31.0", features = ["json"] } | ||
insta = { version = "1.31.0", features = ["json", "ron"] } | ||
serde_json = { workspace = true } | ||
tokio = { workspace = true, features = ["sync", "rt", "macros"] } | ||
velcro = "0.5.4" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# fuel-indexer-graphql-dyn | ||
|
||
GraphQL Dyn is a library for building executable dynamic GraphQL schemas. | ||
|
||
Dyn has two main parts: the `store::Store` and the `schema::DynSchemaBuilder`. The schema builder is responsible for creating executable `async_graphql_dynamic::dynamic::Schema` instances from a dynamically defined `schema::DynSchemaType`. Built schema executes GraphQL queries by fetching data from the store. The store is responsible for communicating with the underlying data source. | ||
|
||
## Usage | ||
|
||
Users of Dyn will need three things to get started: | ||
|
||
```rs | ||
/// A `store::Store` implementation. | ||
#[async_trait] | ||
impl Store for MyStore { | ||
fn r#type(&self) -> &StoreType { ... } | ||
// | ||
async fn obj_get(&self, id: &Id) -> R<O<Obj>> { ... } | ||
async fn obj_get_many(&self, ids: &[Id]) -> R<Vec<O<Obj>>> { ... } | ||
async fn assoc_get(&self, key: &Key, ...) -> R<Vec<O<Assoc>>> { ... } | ||
async fn assoc_count(&self, key: &Key, ...) -> R<u64> { ... } | ||
async fn assoc_range(&self, key: &Key, ...) -> R<Vec<Assoc>> { ... } | ||
async fn assoc_time_range(&self, key: &Key, ...) -> R<Vec<Assoc>> { ... } | ||
} | ||
|
||
/// A `store::StoreType` constructor. | ||
fn new_store_type(config: &MyConfig) -> StoreType { | ||
let mut store_type = StoreTypeBuilder::default(); | ||
... | ||
store_type.finish() | ||
} | ||
|
||
/// A `schema::DynSchemaType` constructor. | ||
fn new_schema_type(config: &MyConfig, store_type: &StoreType) -> DynSchemaType { | ||
let mut schema_type = DynSchemaTypeBuilder::new(store_type); | ||
... | ||
schema_type.finish() | ||
} | ||
``` | ||
|
||
With these implemented, you can create a dynamic schema and execute queries: | ||
|
||
```rs | ||
let store_type = new_store_type(&my_config)?; | ||
let schema_type = new_schema_type(&my_config, &store_type)?; | ||
let store = MyStore::new(&store_type); | ||
|
||
let builder = DynSchemaBuilder::new(&schema_type, Arc::new(Mutex::new(store))); | ||
let schema = builder.finish()?; | ||
|
||
let response = schema.execute(Request::new(r#" | ||
query { | ||
chain(id: "Chain:0") { | ||
name | ||
... | ||
blocks(first: 10) { | ||
nodes { | ||
number | ||
... | ||
transactions(first: 10) { | ||
nodes { | ||
index | ||
... | ||
} | ||
pageInfo { | ||
hasNextPage | ||
hasPreviousPage | ||
startCursor | ||
endCursor | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
"#)).await; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#![allow(clippy::result_unit_err)] | ||
|
||
pub mod schema; | ||
pub mod spec; | ||
pub mod store; | ||
pub mod testing; | ||
|
||
pub(self) mod self_prelude { | ||
pub use anyhow::anyhow; | ||
pub use async_trait::async_trait; | ||
pub use extension_trait::extension_trait; | ||
} |
35 changes: 35 additions & 0 deletions
35
packages/fuel-indexer-graphql-dyn/src/schema/connection.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
use super::node::*; | ||
use super::paging::*; | ||
use super::self_prelude::*; | ||
|
||
#[derive(Clone, PartialEq, Eq)] | ||
pub struct DynConnectionEdge { | ||
pub node_id: DynNodeId, | ||
pub cursor: Cursor, | ||
} | ||
|
||
#[derive(Clone, PartialEq, Eq)] | ||
pub struct DynConnection { | ||
pub total_count: usize, | ||
pub edges: Vec<DynConnectionEdge>, | ||
pub page_info: DynPageInfo, | ||
} | ||
|
||
impl DynConnectionEdge { | ||
pub fn new( | ||
node_id: &(impl Into<DynNodeId> + Clone), | ||
cursor: &(impl Into<Cursor> + Clone), | ||
) -> Self { | ||
Self { | ||
node_id: node_id.clone().into(), | ||
cursor: cursor.clone().into(), | ||
} | ||
} | ||
|
||
pub fn node_id(&self) -> &DynNodeId { | ||
&self.node_id | ||
} | ||
pub fn cursor(&self) -> &Cursor { | ||
&self.cursor | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
use super::self_prelude::*; | ||
|
||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] | ||
pub enum DynDataTypeId { | ||
// GraphQL types | ||
ID, | ||
String, | ||
Int, | ||
Float, | ||
Boolean, | ||
List(Box<DynDataTypeId>), | ||
// Option(Box<DynDataTypeId>), | ||
// Fuel types | ||
Unit, | ||
U8, | ||
U16, | ||
U32, | ||
U64, | ||
B256, | ||
Bytes, | ||
Name(Name), | ||
} | ||
|
||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
pub enum DynDataType { | ||
// GraphQL types | ||
ID, | ||
String, | ||
Int, | ||
Float, | ||
Boolean, | ||
List(Box<DynDataType>), | ||
// Option(Box<DynDataTypeId>), | ||
// Fuel types | ||
Unit, | ||
U8, | ||
U16, | ||
U32, | ||
U64, | ||
B256, | ||
Bytes, | ||
Object(Name, IndexMap<DynDataFieldId, DynDataFieldType>), | ||
Enum(Name, IndexMap<DynDataFieldId, DynDataFieldType>), | ||
} | ||
|
||
pub type DynDataFieldId = String; | ||
|
||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
pub struct DynDataFieldType { | ||
pub name: Name, | ||
pub data_type_id: DynDataTypeId, | ||
pub store_data_field_id: store::DataFieldId, | ||
} | ||
|
||
impl From<store::DataTypeId> for DynDataTypeId { | ||
fn from(store_data_type_id: store::DataTypeId) -> Self { | ||
match store_data_type_id { | ||
store::DataTypeId::Unit => Self::Unit, | ||
store::DataTypeId::Bool => Self::Boolean, | ||
store::DataTypeId::U8 => Self::U8, | ||
store::DataTypeId::U16 => Self::U16, | ||
store::DataTypeId::U32 => Self::U32, | ||
store::DataTypeId::U64 => Self::U64, | ||
store::DataTypeId::B256 => Self::B256, | ||
store::DataTypeId::Byte => Self::U8, | ||
store::DataTypeId::Bytes => Self::Bytes, | ||
store::DataTypeId::String => Self::String, | ||
store::DataTypeId::Array(ty) => Self::List(Box::new((*ty).into())), | ||
store::DataTypeId::Name(name) => Self::Name(name), | ||
} | ||
} | ||
} | ||
|
||
impl FromStr for DynDataTypeId { | ||
type Err = (); | ||
fn from_str(str: &str) -> Result<Self, Self::Err> { | ||
match str { | ||
// GraphQL types | ||
"ID" => Ok(Self::ID), | ||
"String" => Ok(Self::String), | ||
"Int" => Ok(Self::Int), | ||
"Float" => Ok(Self::Float), | ||
"Boolean" => Ok(Self::Boolean), | ||
// List(Box<DynDataTypeId>), | ||
// Option(Box<DynDataTypeId>), | ||
// Fuel types | ||
"Unit" => Ok(Self::Unit), | ||
"U8" => Ok(Self::U8), | ||
"U16" => Ok(Self::U16), | ||
"U32" => Ok(Self::U32), | ||
"U64" => Ok(Self::U64), | ||
"B256" => Ok(Self::B256), | ||
"Bytes" => Ok(Self::Bytes), | ||
str => Ok(Self::Name(Name::new_pascal(str))), | ||
} | ||
} | ||
} | ||
|
||
impl std::fmt::Display for DynDataTypeId { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Self::ID => write!(f, "ID"), | ||
Self::Int => write!(f, "Int"), | ||
Self::Float => write!(f, "Float"), | ||
Self::List(ty) => write!(f, "[{}]", ty), | ||
// Self::Option(ty) => write!(f, "{}", ty), | ||
Self::Unit => write!(f, "Unit"), | ||
Self::Boolean => write!(f, "Boolean"), | ||
Self::U8 => write!(f, "U8"), | ||
Self::U16 => write!(f, "U16"), | ||
Self::U32 => write!(f, "U32"), | ||
Self::U64 => write!(f, "U64"), | ||
Self::B256 => write!(f, "B256"), | ||
Self::Bytes => write!(f, "Bytes"), | ||
Self::String => write!(f, "String"), | ||
Self::Name(str) => write!(f, "{}", str), | ||
} | ||
} | ||
} | ||
|
||
impl DynDataType { | ||
pub fn name(&self) -> Name { | ||
match self { | ||
Self::ID => Name::new_pascal("ID"), | ||
Self::Int => Name::new_pascal("Int"), | ||
Self::Float => Name::new_pascal("Float"), | ||
Self::List(ty) => Name::new_pascal(format!("[{}]", ty.name())), | ||
// Self::Option(ty) => Name::new_pascal(&format!("{}!", ty)), | ||
Self::Unit => Name::new_pascal("Unit"), | ||
Self::Boolean => Name::new_pascal("Boolean"), | ||
Self::U8 => Name::new_pascal("U8"), | ||
Self::U16 => Name::new_pascal("U16"), | ||
Self::U32 => Name::new_pascal("U32"), | ||
Self::U64 => Name::new_pascal("U64"), | ||
Self::B256 => Name::new_pascal("B256"), | ||
Self::Bytes => Name::new_pascal("Bytes"), | ||
Self::String => Name::new_pascal("String"), | ||
Self::Object(name, _) => Name::new_pascal(format!("{}", name)), | ||
Self::Enum(name, _) => Name::new_pascal(format!("{}", name)), | ||
} | ||
} | ||
} | ||
|
||
impl DynDataFieldType { | ||
pub fn data_type_id(&self) -> &DynDataTypeId { | ||
&self.data_type_id | ||
} | ||
pub fn store_id(&self) -> &store::DataFieldId { | ||
&self.store_data_field_id | ||
} | ||
} |
Oops, something went wrong.