From 7ecac2cb5e267df68a666c6e580d2353193b9d77 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Sun, 2 Jun 2024 18:19:34 +0530 Subject: [PATCH 01/20] init --- Cargo.lock | 13 +- Cargo.toml | 5 +- generated/.tailcallrc.schema.json | 2 + src/core/blueprint/blueprint.rs | 24 ++- src/core/http/response.rs | 25 +--- src/core/ir/jit/mod.rs | 236 ++++++++++++++++++++++++++++-- src/core/mod.rs | 55 ++++++- 7 files changed, 316 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6a6cf3307..62553477a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4517,9 +4517,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0218ceea14babe24a4a5836f86ade86c1effbc198164e619194cb5069187e29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -4529,9 +4529,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed5a1ccce8ff962e31a165d41f6e2a2dd1245099dc4d594f5574a86cd90f4d3" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", @@ -4677,9 +4677,9 @@ dependencies = [ [[package]] name = "serde_json_borrow" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256097ff1654ce1975402cb5a2bfa2cad3cc3199e1d704bf303b386fc971f3c" +source = "git+https://github.com/tailcallhq/serde_json_borrow?branch=temp#af929274109c540975921cc3268084914848f272" dependencies = [ + "schemars", "serde", "serde_json", ] @@ -5083,6 +5083,7 @@ dependencies = [ "anyhow", "async-graphql", "async-graphql-extension-apollo-tracing", + "async-graphql-parser", "async-graphql-value", "async-recursion", "async-std", diff --git a/Cargo.toml b/Cargo.toml index aedd364aab..9f589e777f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ tailcall-version = { path = "./tailcall-version", optional = true } # dependencies safe for wasm: rustls-pemfile = { version = "1.0.4" } -schemars = { version = "0.8.17", features = ["derive"] } +schemars = { version = "0.8.21", features = ["derive"] } hyper = { version = "0.14.28", features = ["server"], default-features = false } tokio = { workspace = true } anyhow = { workspace = true } @@ -141,6 +141,7 @@ mime = "0.3.17" htpasswd-verify = { version = "0.3.0", git = "https://github.com/twistedfall/htpasswd-verify", rev = "ff14703083cbd639f7d05622b398926f3e718d61" } # fork version that is wasm compatible jsonwebtoken = "9.3.0" async-graphql-value = "7.0.3" +async-graphql-parser = "7.0.5" async-graphql = { workspace = true, features = [ "dynamic-schema", "dataloader", @@ -157,7 +158,7 @@ datatest-stable = "0.2.9" tokio-test = "0.4.4" base64 = "0.22.1" tailcall-hasher = { path = "tailcall-hasher" } -serde_json_borrow = "0.5.0" +serde_json_borrow = {git = "https://github.com/tailcallhq/serde_json_borrow", branch = "temp"} [dev-dependencies] tailcall-prettier = { path = "tailcall-prettier" } diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index 83b13b251f..0fa6af467d 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -489,6 +489,7 @@ }, "protected": { "description": "Marks field as protected by auth provider", + "default": null, "anyOf": [ { "$ref": "#/definitions/Protected" @@ -1266,6 +1267,7 @@ }, "protected": { "description": "Marks field as protected by auth providers", + "default": null, "anyOf": [ { "$ref": "#/definitions/Protected" diff --git a/src/core/blueprint/blueprint.rs b/src/core/blueprint/blueprint.rs index 503a4eda86..8b3589dc06 100644 --- a/src/core/blueprint/blueprint.rs +++ b/src/core/blueprint/blueprint.rs @@ -1,4 +1,5 @@ use std::collections::{BTreeSet, HashMap}; +use std::fmt::{Debug, Formatter}; use std::sync::Arc; use async_graphql::dynamic::{Schema, SchemaBuilder}; @@ -27,12 +28,33 @@ pub struct Blueprint { pub telemetry: Telemetry, } -#[derive(Clone, Debug)] +#[derive(Clone)] pub enum Type { NamedType { name: String, non_null: bool }, ListType { of_type: Box, non_null: bool }, } +impl Debug for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Type::NamedType { name, non_null } => { + if *non_null { + write!(f, "{}!", name) + } else { + write!(f, "{}", name) + } + } + Type::ListType { of_type, non_null } => { + if *non_null { + write!(f, "[{:?}]!", of_type) + } else { + write!(f, "[{:?}]", of_type) + } + } + } + } +} + impl Default for Type { fn default() -> Self { Type::NamedType { name: "JSON".to_string(), non_null: false } diff --git a/src/core/http/response.rs b/src/core/http/response.rs index 7f0377cad5..0dc6479d9f 100644 --- a/src/core/http/response.rs +++ b/src/core/http/response.rs @@ -1,3 +1,4 @@ + use anyhow::Result; use async_graphql_value::{ConstValue, Name}; use derive_setters::Setters; @@ -9,6 +10,7 @@ use tonic_types::Status as GrpcStatus; use crate::core::grpc::protobuf::ProtobufOperation; use crate::core::ir::EvaluationError; +use crate::core::{FromValue}; #[derive(Clone, Debug, Default, Setters)] pub struct Response { @@ -23,29 +25,6 @@ pub struct Response { // efficient. Benchmarking is required to determine the performance If any // change is made. -pub trait FromValue { - fn from_value(value: serde_json_borrow::Value) -> Self; -} - -impl FromValue for ConstValue { - fn from_value(value: serde_json_borrow::Value) -> Self { - match value { - serde_json_borrow::Value::Null => ConstValue::Null, - serde_json_borrow::Value::Bool(b) => ConstValue::Boolean(b), - serde_json_borrow::Value::Number(n) => ConstValue::Number(n.into()), - serde_json_borrow::Value::Str(s) => ConstValue::String(s.into()), - serde_json_borrow::Value::Array(a) => { - ConstValue::List(a.into_iter().map(|v| Self::from_value(v)).collect()) - } - serde_json_borrow::Value::Object(o) => ConstValue::Object( - o.iter() - .map(|(k, v)| (Name::new(k), Self::from_value(v.to_owned()))) - .collect(), - ), - } - } -} - impl Response { pub async fn from_reqwest(resp: reqwest::Response) -> Result { let status = resp.status(); diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 6c422d03c2..7b153ec3ad 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -7,61 +7,277 @@ /// made. mod model { - use async_graphql::parser::types::ExecutableDocument; + use std::collections::HashMap; + use std::fmt::{Debug, Formatter}; + use async_graphql::parser::types::{DocumentOperations, ExecutableDocument, Selection}; + use async_graphql::Positioned; + + use serde_json_borrow::{OwnedValue}; + + use crate::core::blueprint::{Blueprint, Definition, FieldDefinition, InputFieldDefinition}; use crate::core::ir::IR; + use crate::core::merge_right::MergeRight; + use crate::core::FromValue; + + trait IncrGen { + fn gen(&mut self) -> Self; + } + #[derive(Debug)] pub enum Type { Named(String), List(Box), Required(Box), } + #[derive(Debug)] pub struct Arg { pub id: ArgId, pub name: String, - pub type_of: Type, + pub type_of: crate::core::blueprint::Type, + pub value: Option, + pub default_value: Option, } pub struct ArgId(usize); + + impl Debug for ArgId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + + impl IncrGen for ArgId { + fn gen(&mut self) -> Self { + let id = self.0; + self.0 += 1; + Self(id) + } + } + impl ArgId { fn new(id: usize) -> Self { ArgId(id) } } + trait Id { + fn as_usize(&self) -> usize; + } + #[derive(Clone, PartialEq, Eq)] pub struct FieldId(usize); + + impl Debug for FieldId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + impl FieldId { fn new(id: usize) -> Self { FieldId(id) } } + impl IncrGen for FieldId { + fn gen(&mut self) -> Self { + let id = self.0; + self.0 += 1; + Self(id) + } + } + pub struct Field { pub id: FieldId, pub name: String, pub ir: Option, - pub type_of: Type, + pub type_of: crate::core::blueprint::Type, pub args: Vec, pub refs: Option, } + impl Debug for Field { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut debug_struct = f.debug_struct("Field"); + debug_struct.field("id", &self.id); + debug_struct.field("name", &self.name); + if self.ir.is_some() { + debug_struct.field("ir", &"Some(..)"); + } + debug_struct.field("type_of", &self.type_of); + if !self.args.is_empty() { + debug_struct.field("args", &self.args); + } + if self.refs.is_some() { + debug_struct.field("refs", &"Some(..)"); + } + debug_struct.finish() + } + } + + #[derive(Debug)] pub struct Parent(FieldId); + pub struct Children(Vec); + #[derive(Debug)] pub struct QueryBlueprint { pub fields: Vec>, } - impl From for QueryBlueprint { - fn from(value: ExecutableDocument) -> Self { - // TODO: @ssddOnTop - // 1. Handle all edge-cases - // 2. Add unit tests - todo!() + impl QueryBlueprint { + pub fn from_document(document: ExecutableDocument, blueprint: Blueprint) -> Self { + let fields = convert_query_to_field(document, &blueprint.definitions).unwrap(); + Self { fields } } } + + fn convert_query_to_field( + document: ExecutableDocument, + schema_definitions: &[Definition], + ) -> anyhow::Result>> { + let mut id = FieldId::new(0); + let mut arg_id = ArgId::new(0); + + let mut fields = Vec::new(); + + fn resolve_selection_set( + selection_set: Positioned, + schema_definitions: &[Definition], + id: &mut FieldId, + arg_id: &mut ArgId, + ) -> Vec> { + let mut fields = Vec::new(); + + for selection in selection_set.node.items { + if let Selection::Field(gql_field) = selection.node { + let field_name = gql_field.node.name.node.as_str(); + let field_args = gql_field + .node + .arguments + .into_iter() + .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const().unwrap())) + .collect::>(); + + if let Some(definition) = find_definition(field_name, schema_definitions) { + let mut args = vec![]; + field_args.into_iter().for_each(|(k, v)| { + if let Some(arg) = find_definition_arg(&k, schema_definitions) { + let type_of = arg.of_type.clone(); + let id = arg_id.gen(); + let arg = Arg { + id, + name: k, + type_of, + value: Some(v.into_bvalue().into()), + default_value: None, + }; + args.push(arg); + } + }); + + let type_of = definition.of_type.clone(); + fields = fields.merge_right(resolve_selection_set( + gql_field.node.selection_set.clone(), + schema_definitions, + id, + arg_id, + )); + + let id = id.gen(); + let field = Field { + id, + name: field_name.to_string(), + ir: definition.resolver.clone(), + type_of, + args, + refs: None, + }; + fields.push(field); + } + } + } + + fields + } + + match document.operations { + DocumentOperations::Single(single) => { + fields = resolve_selection_set( + single.node.selection_set, + schema_definitions, + &mut id, + &mut arg_id, + ); + } + DocumentOperations::Multiple(_) => {} + } + + Ok(fields) + } + + fn find_definition<'a>( + name: &str, + definitions: &'a [Definition], + ) -> Option<&'a FieldDefinition> { + for def in definitions { + if let Definition::Object(object) = def { + for field in &object.fields { + if field.name == name { + return Some(field); + } + } + } + } + None + } + + fn find_definition_arg<'a>( + name: &str, + definitions: &'a [Definition], + ) -> Option<&'a InputFieldDefinition> { + for def in definitions { + if let Definition::Object(object) = def { + for field in &object.fields { + for arg in &field.args { + if arg.name == name { + return Some(arg); + } + } + } + } + } + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::core::blueprint::Blueprint; + use crate::core::config::reader::ConfigReader; + + #[tokio::test] + async fn test_from_document() { + let rt = crate::core::runtime::test::init(None); + let reader = ConfigReader::init(rt); + let config = reader + .read("examples/jsonplaceholder.graphql") + .await + .unwrap(); + let blueprint = Blueprint::try_from(&config).unwrap(); + let query = r#" + query { + posts { user { id } } + } + "#; + let document = async_graphql::parser::parse_query(query).unwrap(); + let q_blueprint = model::QueryBlueprint::from_document(document, blueprint); + // insta::assert_snapshot!(); + println!("{:#?}", q_blueprint); + } } mod value { @@ -69,7 +285,6 @@ mod value { } mod cache { - use super::model::FieldId; use super::value::OwnedValue; @@ -172,7 +387,6 @@ mod executor { } mod synth { - pub use serde_json_borrow::*; use super::cache::Cache; diff --git a/src/core/mod.rs b/src/core/mod.rs index f715bc7c52..c4f4868d7a 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -41,11 +41,64 @@ pub mod worker; use std::borrow::Cow; use std::hash::Hash; use std::num::NonZeroU64; +use std::str::FromStr; -use async_graphql_value::ConstValue; +use async_graphql_value::{ConstValue, Name}; use http::Response; pub use tailcall_macros as macros; +pub type BorrowedValue = serde_json_borrow::Value<'static>; + +pub trait FromValue { + fn from_value(value: serde_json_borrow::Value) -> Self; + fn into_bvalue(self) -> BorrowedValue; +} + +impl FromValue for async_graphql_value::ConstValue { + fn from_value(value: serde_json_borrow::Value) -> Self { + match value { + serde_json_borrow::Value::Null => ConstValue::Null, + serde_json_borrow::Value::Bool(b) => ConstValue::Boolean(b), + serde_json_borrow::Value::Number(n) => ConstValue::Number(n.into()), + serde_json_borrow::Value::Str(s) => ConstValue::String(s.into()), + serde_json_borrow::Value::Array(a) => { + ConstValue::List(a.into_iter().map(|v| Self::from_value(v)).collect()) + } + serde_json_borrow::Value::Object(o) => ConstValue::Object( + o.iter() + .map(|(k, v)| (Name::new(k), Self::from_value(v.to_owned()))) + .collect(), + ), + } + } + + fn into_bvalue(self) -> BorrowedValue { + match self { + async_graphql_value::ConstValue::Null => serde_json_borrow::Value::Null, + async_graphql_value::ConstValue::Boolean(b) => serde_json_borrow::Value::Bool(b), + async_graphql_value::ConstValue::Number(n) => serde_json_borrow::Value::Number( + serde_json_borrow::Number::from_str(&n.to_string()).unwrap(), + ), // TODO: FIXME + async_graphql_value::ConstValue::String(s) => { + serde_json_borrow::Value::Str(Cow::Owned(s)) + } + async_graphql_value::ConstValue::List(a) => serde_json_borrow::Value::Array( + a.into_iter() + .map(|v| v.into_bvalue()) + .collect::>(), + ), + async_graphql_value::ConstValue::Object(o) => serde_json_borrow::Value::Object( + o.into_iter() + .map(|(k, v)| (k.to_string(), v.into_bvalue())) + .collect::>() + .into(), + ), + async_graphql_value::ConstValue::Binary(_) => todo!(), + async_graphql_value::ConstValue::Enum(_) => todo!(), + } + } +} + pub trait EnvIO: Send + Sync + 'static { fn get(&self, key: &str) -> Option>; } From a177f741150e5b2e45e3477f254d85072b90280f Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Sun, 2 Jun 2024 19:22:17 +0530 Subject: [PATCH 02/20] init --- src/core/blueprint/blueprint_index.rs | 78 +++++++++++++++ src/core/blueprint/mod.rs | 2 + src/core/ir/jit/mod.rs | 131 +++++++++++++++++++++++--- 3 files changed, 199 insertions(+), 12 deletions(-) create mode 100644 src/core/blueprint/blueprint_index.rs diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/blueprint/blueprint_index.rs new file mode 100644 index 0000000000..ad7f0b4325 --- /dev/null +++ b/src/core/blueprint/blueprint_index.rs @@ -0,0 +1,78 @@ +use std::collections::HashMap; +use crate::core::blueprint::{Definition, FieldDefinition, InputFieldDefinition}; + +#[derive(Debug)] +pub struct BlueprintIndex { + pub map: HashMap)> +} + +#[derive(Debug)] +pub enum FieldDef { + Field(FieldDefinition), + InputFieldDefinition(InputFieldDefinition) +} + +impl BlueprintIndex { + pub fn init(definitions: Vec) -> Self { + let mut map = HashMap::new(); + + for definition in definitions { + match definition { + Definition::Object(object_def) => { + let type_name = object_def.name.clone(); + let mut fields_map = HashMap::new(); + + for field in &object_def.fields { + fields_map.insert(field.name.clone(), FieldDef::Field(field.clone())); + } + + map.insert(type_name, (Definition::Object(object_def), fields_map)); + } + Definition::Interface(interface_def) => { + let type_name = interface_def.name.clone(); + let mut fields_map = HashMap::new(); + + for field in interface_def.fields.clone() { + fields_map.insert(field.name.clone(), FieldDef::Field(field)); + } + + map.insert(type_name, (Definition::Interface(interface_def), fields_map)); + } + Definition::InputObject(input_object_def) => { + let type_name = input_object_def.name.clone(); + let mut fields_map = HashMap::new(); + + for field in input_object_def.fields.clone() { + fields_map.insert(field.name.clone(), FieldDef::InputFieldDefinition(field)); + } + + map.insert(type_name, (Definition::InputObject(input_object_def), fields_map)); + } + Definition::Scalar(scalar_def) => { + let type_name = scalar_def.name.clone(); + map.insert(type_name.clone(), (Definition::Scalar(scalar_def), HashMap::new())); + } + Definition::Enum(enum_def) => { + let type_name = enum_def.name.clone(); + map.insert(type_name.clone(), (Definition::Enum(enum_def), HashMap::new())); + } + Definition::Union(union_def) => { + let type_name = union_def.name.clone(); + map.insert(type_name.clone(), (Definition::Union(union_def), HashMap::new())); + } + } + } + + Self { + map + } + } + + pub fn get_type(&self, type_name: &str) -> Option<&Definition> { + self.map.get(type_name).map(|(definition, _)| definition) + } + + pub fn get_field(&self, query: &str, field_name: &str) -> Option<&FieldDef> { + self.map.get(query).and_then(|(_, fields_map)| fields_map.get(field_name)) + } +} \ No newline at end of file diff --git a/src/core/blueprint/mod.rs b/src/core/blueprint/mod.rs index 4af6452542..428e7d961a 100644 --- a/src/core/blueprint/mod.rs +++ b/src/core/blueprint/mod.rs @@ -14,9 +14,11 @@ mod server; pub mod telemetry; mod timeout; mod upstream; +mod blueprint_index; pub use auth::*; pub use blueprint::*; +pub use blueprint_index::*; pub use cors::*; pub use definitions::*; pub use dynamic_value::*; diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 7b153ec3ad..5066b8de3f 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -12,10 +12,10 @@ mod model { use async_graphql::parser::types::{DocumentOperations, ExecutableDocument, Selection}; use async_graphql::Positioned; - + use serde_json_borrow::{OwnedValue}; - use crate::core::blueprint::{Blueprint, Definition, FieldDefinition, InputFieldDefinition}; + use crate::core::blueprint::{Blueprint, BlueprintIndex, Definition, FieldDef, FieldDefinition, InputFieldDefinition}; use crate::core::ir::IR; use crate::core::merge_right::MergeRight; use crate::core::FromValue; @@ -128,12 +128,115 @@ mod model { } impl QueryBlueprint { - pub fn from_document(document: ExecutableDocument, blueprint: Blueprint) -> Self { - let fields = convert_query_to_field(document, &blueprint.definitions).unwrap(); + pub fn from_document(document: ExecutableDocument, defs: &[Definition]) -> Self { + let fields = convert_query_to_field(document, defs).unwrap(); + Self { fields } + } + + pub fn from_document1(document: ExecutableDocument, bpi: BlueprintIndex, query: &str) -> Self { + let fields = convert_query_to_field1(document, &bpi, query).unwrap(); Self { fields } } } + fn convert_query_to_field1( + document: ExecutableDocument, + blueprint_index: &BlueprintIndex, + query: &str, + ) -> anyhow::Result>> { + let mut id = FieldId::new(0); + let mut arg_id = ArgId::new(0); + + let mut fields = Vec::new(); + + fn resolve_selection_set( + selection_set: Positioned, + blueprint_index: &BlueprintIndex, + id: &mut FieldId, + arg_id: &mut ArgId, + current_type: &str, + ) -> Vec> { + let mut fields = Vec::new(); + + for selection in selection_set.node.items { + if let Selection::Field(gql_field) = selection.node { + let field_name = gql_field.node.name.node.as_str(); + let field_args = gql_field + .node + .arguments + .into_iter() + .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const().unwrap())) + .collect::>(); + println!("hx: {}", current_type); + + if let Some((_, fields_map)) = blueprint_index.map.get(current_type) { + if let Some(field_def) = fields_map.get(field_name) { + let mut args = vec![]; + for (arg_name, v) in field_args { + if let Some(FieldDef::InputFieldDefinition(arg)) = fields_map.get(&arg_name) { + let type_of = arg.of_type.clone(); + let id = arg_id.gen(); + let arg = Arg { + id, + name: arg_name.clone(), + type_of, + value: Some(v.into_bvalue().into()), + default_value: None, // TODO: + }; + args.push(arg); + } + } + + let type_of = match field_def { + FieldDef::Field(field_def) => field_def.of_type.clone(), + FieldDef::InputFieldDefinition(field_def) => field_def.of_type.clone(), + }; + + fields = fields.merge_right(resolve_selection_set( + gql_field.node.selection_set.clone(), + blueprint_index, + id, + arg_id, + type_of.name(), + )); + + let id = id.gen(); + let field = Field { + id, + name: field_name.to_string(), + ir: match field_def { + FieldDef::Field(field_def) => field_def.resolver.clone(), + _ => None, + }, + type_of, + args, + refs: None, + }; + fields.push(field); + } + } + } + } + + fields + } + + match document.operations { + DocumentOperations::Single(single) => { + fields = resolve_selection_set( + single.node.selection_set, + blueprint_index, + &mut id, + &mut arg_id, + query, + ); + } + DocumentOperations::Multiple(_) => {} + } + + Ok(fields) + } + fn convert_query_to_field( document: ExecutableDocument, schema_definitions: &[Definition], @@ -256,7 +359,7 @@ mod model { #[cfg(test)] mod tests { use super::*; - use crate::core::blueprint::Blueprint; + use crate::core::blueprint::{Blueprint, BlueprintIndex}; use crate::core::config::reader::ConfigReader; #[tokio::test] @@ -274,8 +377,12 @@ mod tests { } "#; let document = async_graphql::parser::parse_query(query).unwrap(); - let q_blueprint = model::QueryBlueprint::from_document(document, blueprint); + let bp_index = BlueprintIndex::init(blueprint.definitions.clone()); + // println!("{:#?}", bp_index); + let q_blueprint = model::QueryBlueprint::from_document(document.clone(), &blueprint.definitions); + let q_blueprint1 = model::QueryBlueprint::from_document1(document, bp_index, "Query"); // insta::assert_snapshot!(); + println!("{:#?}", q_blueprint1); println!("{:#?}", q_blueprint); } } @@ -354,9 +461,9 @@ mod executor { .into_iter() .map(|child| self.execute_field(child.id, Some(&value))), ) - .await - .into_iter() - .collect::>>()?; + .await + .into_iter() + .collect::>>()?; self.insert_field_value(id, value); } @@ -378,9 +485,9 @@ mod executor { .iter() .map(|field| self.execute_field(field.id.to_owned(), None)), ) - .await - .into_iter() - .collect::>>()?; + .await + .into_iter() + .collect::>>()?; Ok(()) } } From 051681b6b88abc1a768e2fcf1ed8527db4d7b00c Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Sun, 2 Jun 2024 20:19:12 +0530 Subject: [PATCH 03/20] impl multiple operations --- src/core/ir/jit/mod.rs | 14 ++++++++--- ...__core__ir__jit__tests__from_document.snap | 25 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/core/ir/jit/snapshots/tailcall__core__ir__jit__tests__from_document.snap diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 7b153ec3ad..64db365ec0 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -212,7 +212,16 @@ mod model { &mut arg_id, ); } - DocumentOperations::Multiple(_) => {} + DocumentOperations::Multiple(multiple) => { + for (_, single) in multiple { + fields = resolve_selection_set( + single.node.selection_set, + schema_definitions, + &mut id, + &mut arg_id, + ); + } + } } Ok(fields) @@ -275,8 +284,7 @@ mod tests { "#; let document = async_graphql::parser::parse_query(query).unwrap(); let q_blueprint = model::QueryBlueprint::from_document(document, blueprint); - // insta::assert_snapshot!(); - println!("{:#?}", q_blueprint); + insta::assert_snapshot!(format!("{:#?}", q_blueprint)); } } diff --git a/src/core/ir/jit/snapshots/tailcall__core__ir__jit__tests__from_document.snap b/src/core/ir/jit/snapshots/tailcall__core__ir__jit__tests__from_document.snap new file mode 100644 index 0000000000..0ca8e91ff5 --- /dev/null +++ b/src/core/ir/jit/snapshots/tailcall__core__ir__jit__tests__from_document.snap @@ -0,0 +1,25 @@ +--- +source: src/core/ir/jit/mod.rs +expression: "format!(\"{:#?}\", q_blueprint)" +--- +QueryBlueprint { + fields: [ + Field { + id: 0, + name: "id", + type_of: Int!, + }, + Field { + id: 1, + name: "user", + ir: "Some(..)", + type_of: User, + }, + Field { + id: 2, + name: "posts", + ir: "Some(..)", + type_of: [Post], + }, + ], +} From d5929937a2f71e46eb244f7d2191150386427e3b Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 11:06:38 +0530 Subject: [PATCH 04/20] partial cleanup --- src/core/blueprint/blueprint_index.rs | 43 +++-- src/core/blueprint/mod.rs | 2 +- src/core/http/response.rs | 3 +- src/core/ir/jit/mod.rs | 229 ++++++++------------------ src/core/mod.rs | 4 +- 5 files changed, 106 insertions(+), 175 deletions(-) diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/blueprint/blueprint_index.rs index ad7f0b4325..55460e64e2 100644 --- a/src/core/blueprint/blueprint_index.rs +++ b/src/core/blueprint/blueprint_index.rs @@ -1,15 +1,16 @@ use std::collections::HashMap; + use crate::core::blueprint::{Definition, FieldDefinition, InputFieldDefinition}; #[derive(Debug)] pub struct BlueprintIndex { - pub map: HashMap)> + pub map: HashMap)>, } #[derive(Debug)] pub enum FieldDef { Field(FieldDefinition), - InputFieldDefinition(InputFieldDefinition) + InputFieldDefinition(InputFieldDefinition), } impl BlueprintIndex { @@ -36,36 +37,50 @@ impl BlueprintIndex { fields_map.insert(field.name.clone(), FieldDef::Field(field)); } - map.insert(type_name, (Definition::Interface(interface_def), fields_map)); + map.insert( + type_name, + (Definition::Interface(interface_def), fields_map), + ); } Definition::InputObject(input_object_def) => { let type_name = input_object_def.name.clone(); let mut fields_map = HashMap::new(); for field in input_object_def.fields.clone() { - fields_map.insert(field.name.clone(), FieldDef::InputFieldDefinition(field)); + fields_map + .insert(field.name.clone(), FieldDef::InputFieldDefinition(field)); } - map.insert(type_name, (Definition::InputObject(input_object_def), fields_map)); + map.insert( + type_name, + (Definition::InputObject(input_object_def), fields_map), + ); } Definition::Scalar(scalar_def) => { let type_name = scalar_def.name.clone(); - map.insert(type_name.clone(), (Definition::Scalar(scalar_def), HashMap::new())); + map.insert( + type_name.clone(), + (Definition::Scalar(scalar_def), HashMap::new()), + ); } Definition::Enum(enum_def) => { let type_name = enum_def.name.clone(); - map.insert(type_name.clone(), (Definition::Enum(enum_def), HashMap::new())); + map.insert( + type_name.clone(), + (Definition::Enum(enum_def), HashMap::new()), + ); } Definition::Union(union_def) => { let type_name = union_def.name.clone(); - map.insert(type_name.clone(), (Definition::Union(union_def), HashMap::new())); + map.insert( + type_name.clone(), + (Definition::Union(union_def), HashMap::new()), + ); } } } - Self { - map - } + Self { map } } pub fn get_type(&self, type_name: &str) -> Option<&Definition> { @@ -73,6 +88,8 @@ impl BlueprintIndex { } pub fn get_field(&self, query: &str, field_name: &str) -> Option<&FieldDef> { - self.map.get(query).and_then(|(_, fields_map)| fields_map.get(field_name)) + self.map + .get(query) + .and_then(|(_, fields_map)| fields_map.get(field_name)) } -} \ No newline at end of file +} diff --git a/src/core/blueprint/mod.rs b/src/core/blueprint/mod.rs index 428e7d961a..c9a65c026a 100644 --- a/src/core/blueprint/mod.rs +++ b/src/core/blueprint/mod.rs @@ -1,5 +1,6 @@ mod auth; mod blueprint; +mod blueprint_index; mod compress; mod cors; mod definitions; @@ -14,7 +15,6 @@ mod server; pub mod telemetry; mod timeout; mod upstream; -mod blueprint_index; pub use auth::*; pub use blueprint::*; diff --git a/src/core/http/response.rs b/src/core/http/response.rs index 0dc6479d9f..c9971badc8 100644 --- a/src/core/http/response.rs +++ b/src/core/http/response.rs @@ -1,4 +1,3 @@ - use anyhow::Result; use async_graphql_value::{ConstValue, Name}; use derive_setters::Setters; @@ -10,7 +9,7 @@ use tonic_types::Status as GrpcStatus; use crate::core::grpc::protobuf::ProtobufOperation; use crate::core::ir::EvaluationError; -use crate::core::{FromValue}; +use crate::core::FromValue; #[derive(Clone, Debug, Default, Setters)] pub struct Response { diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 11cdf56f2c..62c51564ed 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -1,3 +1,4 @@ +#![allow(unused)] /// /// We need three executors for each query /// 1. Global general purpose executor (WE have this currently) @@ -12,10 +13,11 @@ mod model { use async_graphql::parser::types::{DocumentOperations, ExecutableDocument, Selection}; use async_graphql::Positioned; + use serde_json_borrow::OwnedValue; - use serde_json_borrow::{OwnedValue}; - - use crate::core::blueprint::{Blueprint, BlueprintIndex, Definition, FieldDef, FieldDefinition, InputFieldDefinition}; + use crate::core::blueprint::{ + Blueprint, BlueprintIndex, Definition, FieldDef, FieldDefinition, InputFieldDefinition, + }; use crate::core::ir::IR; use crate::core::merge_right::MergeRight; use crate::core::FromValue; @@ -128,172 +130,77 @@ mod model { } impl QueryBlueprint { - pub fn from_document(document: ExecutableDocument, defs: &[Definition]) -> Self { - let fields = convert_query_to_field(document, defs).unwrap(); - Self { fields } - } - - pub fn from_document1(document: ExecutableDocument, bpi: BlueprintIndex, query: &str) -> Self { + pub fn from_document( + document: ExecutableDocument, + bpi: BlueprintIndex, + query: &str, + ) -> Self { let fields = convert_query_to_field1(document, &bpi, query).unwrap(); Self { fields } } } - fn convert_query_to_field1( - document: ExecutableDocument, + #[allow(clippy::too_many_arguments)] + fn resolve_selection_set( + selection_set: Positioned, blueprint_index: &BlueprintIndex, - query: &str, - ) -> anyhow::Result>> { - let mut id = FieldId::new(0); - let mut arg_id = ArgId::new(0); - - let mut fields = Vec::new(); - - fn resolve_selection_set( - selection_set: Positioned, - blueprint_index: &BlueprintIndex, - id: &mut FieldId, - arg_id: &mut ArgId, - current_type: &str, - ) -> Vec> { - let mut fields = Vec::new(); - - for selection in selection_set.node.items { - if let Selection::Field(gql_field) = selection.node { - let field_name = gql_field.node.name.node.as_str(); - let field_args = gql_field - .node - .arguments - .into_iter() - .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const().unwrap())) - .collect::>(); - println!("hx: {}", current_type); - - if let Some((_, fields_map)) = blueprint_index.map.get(current_type) { - if let Some(field_def) = fields_map.get(field_name) { - let mut args = vec![]; - for (arg_name, v) in field_args { - if let Some(FieldDef::InputFieldDefinition(arg)) = fields_map.get(&arg_name) { - let type_of = arg.of_type.clone(); - let id = arg_id.gen(); - let arg = Arg { - id, - name: arg_name.clone(), - type_of, - value: Some(v.into_bvalue().into()), - default_value: None, // TODO: - }; - args.push(arg); - } - } - - let type_of = match field_def { - FieldDef::Field(field_def) => field_def.of_type.clone(), - FieldDef::InputFieldDefinition(field_def) => field_def.of_type.clone(), - }; - - fields = fields.merge_right(resolve_selection_set( - gql_field.node.selection_set.clone(), - blueprint_index, - id, - arg_id, - type_of.name(), - )); - - let id = id.gen(); - let field = Field { - id, - name: field_name.to_string(), - ir: match field_def { - FieldDef::Field(field_def) => field_def.resolver.clone(), - _ => None, - }, - type_of, - args, - refs: None, - }; - fields.push(field); - } - } - } - } - - fields - } - - match document.operations { - DocumentOperations::Single(single) => { - fields = resolve_selection_set( - single.node.selection_set, - blueprint_index, - &mut id, - &mut arg_id, - query, - ); - } - DocumentOperations::Multiple(_) => {} - } - - Ok(fields) - } - - fn convert_query_to_field( - document: ExecutableDocument, - schema_definitions: &[Definition], - ) -> anyhow::Result>> { - let mut id = FieldId::new(0); - let mut arg_id = ArgId::new(0); - + id: &mut FieldId, + arg_id: &mut ArgId, + current_type: &str, + ) -> Vec> { let mut fields = Vec::new(); - fn resolve_selection_set( - selection_set: Positioned, - schema_definitions: &[Definition], - id: &mut FieldId, - arg_id: &mut ArgId, - ) -> Vec> { - let mut fields = Vec::new(); - - for selection in selection_set.node.items { - if let Selection::Field(gql_field) = selection.node { - let field_name = gql_field.node.name.node.as_str(); - let field_args = gql_field - .node - .arguments - .into_iter() - .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const().unwrap())) - .collect::>(); - - if let Some(definition) = find_definition(field_name, schema_definitions) { + for selection in selection_set.node.items { + if let Selection::Field(gql_field) = selection.node { + let field_name = gql_field.node.name.node.as_str(); + let field_args = gql_field + .node + .arguments + .into_iter() + .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const().unwrap())) + .collect::>(); + + if let Some((_, fields_map)) = blueprint_index.map.get(current_type) { + if let Some(field_def) = fields_map.get(field_name) { let mut args = vec![]; - field_args.into_iter().for_each(|(k, v)| { - if let Some(arg) = find_definition_arg(&k, schema_definitions) { + for (arg_name, v) in field_args { + if let Some(FieldDef::InputFieldDefinition(arg)) = + fields_map.get(&arg_name) + { let type_of = arg.of_type.clone(); let id = arg_id.gen(); let arg = Arg { id, - name: k, + name: arg_name.clone(), type_of, value: Some(v.into_bvalue().into()), - default_value: None, + default_value: None, // TODO: }; args.push(arg); } - }); + } + + let type_of = match field_def { + FieldDef::Field(field_def) => field_def.of_type.clone(), + FieldDef::InputFieldDefinition(field_def) => field_def.of_type.clone(), + }; - let type_of = definition.of_type.clone(); fields = fields.merge_right(resolve_selection_set( gql_field.node.selection_set.clone(), - schema_definitions, + blueprint_index, id, arg_id, + type_of.name(), )); let id = id.gen(); let field = Field { id, name: field_name.to_string(), - ir: definition.resolver.clone(), + ir: match field_def { + FieldDef::Field(field_def) => field_def.resolver.clone(), + _ => None, + }, type_of, args, refs: None, @@ -302,28 +209,41 @@ mod model { } } } - - fields } + fields + } + + fn convert_query_to_field1( + document: ExecutableDocument, + blueprint_index: &BlueprintIndex, + query: &str, + ) -> anyhow::Result>> { + let mut id = FieldId::new(0); + let mut arg_id = ArgId::new(0); + + let mut fields = Vec::new(); + match document.operations { DocumentOperations::Single(single) => { fields = resolve_selection_set( single.node.selection_set, - schema_definitions, + blueprint_index, &mut id, &mut arg_id, + query, ); } DocumentOperations::Multiple(multiple) => { - for (_, single) in multiple { + multiple.into_iter().for_each(|(_, single)| { fields = resolve_selection_set( single.node.selection_set, - schema_definitions, + blueprint_index, &mut id, &mut arg_id, + query, ); - } + }); } } @@ -387,10 +307,7 @@ mod tests { "#; let document = async_graphql::parser::parse_query(query).unwrap(); let bp_index = BlueprintIndex::init(blueprint.definitions.clone()); - // println!("{:#?}", bp_index); - let q_blueprint = model::QueryBlueprint::from_document(document.clone(), &blueprint.definitions); - let q_blueprint1 = model::QueryBlueprint::from_document1(document, bp_index, "Query"); - // insta::assert_snapshot!(); + let q_blueprint = model::QueryBlueprint::from_document(document, bp_index, "Query"); insta::assert_snapshot!(format!("{:#?}", q_blueprint)); } } @@ -469,9 +386,9 @@ mod executor { .into_iter() .map(|child| self.execute_field(child.id, Some(&value))), ) - .await - .into_iter() - .collect::>>()?; + .await + .into_iter() + .collect::>>()?; self.insert_field_value(id, value); } @@ -493,9 +410,9 @@ mod executor { .iter() .map(|field| self.execute_field(field.id.to_owned(), None)), ) - .await - .into_iter() - .collect::>>()?; + .await + .into_iter() + .collect::>>()?; Ok(()) } } diff --git a/src/core/mod.rs b/src/core/mod.rs index c4f4868d7a..a5ed499ddc 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -83,9 +83,7 @@ impl FromValue for async_graphql_value::ConstValue { serde_json_borrow::Value::Str(Cow::Owned(s)) } async_graphql_value::ConstValue::List(a) => serde_json_borrow::Value::Array( - a.into_iter() - .map(|v| v.into_bvalue()) - .collect::>(), + a.into_iter().map(|v| v.into_bvalue()).collect::>(), ), async_graphql_value::ConstValue::Object(o) => serde_json_borrow::Value::Object( o.into_iter() From a2649a6bd3454e80dbffae4f702e90e34b97a2fd Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 11:29:57 +0530 Subject: [PATCH 05/20] partial cleanup --- Cargo.lock | 1 - Cargo.toml | 3 ++- generated/.tailcallrc.schema.json | 14 +++++++++++++- src/core/blueprint/blueprint.rs | 3 ++- src/core/config/config.rs | 3 ++- src/core/config/from_document.rs | 5 ++++- src/core/ir/jit/mod.rs | 2 +- 7 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed95c2da2a..9b0793a3b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4671,7 +4671,6 @@ dependencies = [ [[package]] name = "serde_json_borrow" version = "0.5.0" -source = "git+https://github.com/tailcallhq/serde_json_borrow?branch=temp#af929274109c540975921cc3268084914848f272" dependencies = [ "schemars", "serde", diff --git a/Cargo.toml b/Cargo.toml index 9f589e777f..1b8b41a950 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,8 @@ datatest-stable = "0.2.9" tokio-test = "0.4.4" base64 = "0.22.1" tailcall-hasher = { path = "tailcall-hasher" } -serde_json_borrow = {git = "https://github.com/tailcallhq/serde_json_borrow", branch = "temp"} +#serde_json_borrow = {git = "https://github.com/tailcallhq/serde_json_borrow", branch = "temp"} +serde_json_borrow = {path = "../serde_json_borrow"} [dev-dependencies] tailcall-prettier = { path = "tailcall-prettier" } diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index 0fa6af467d..d243fbaf14 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -137,7 +137,16 @@ "type" ], "properties": { - "default_value": true, + "default_value": { + "anyOf": [ + { + "$ref": "#/definitions/OwnedValue" + }, + { + "type": "null" + } + ] + }, "doc": { "type": [ "string", @@ -880,6 +889,9 @@ } } }, + "OwnedValue": { + "type": "object" + }, "PhoneNumber": { "title": "PhoneNumber", "type": "object", diff --git a/src/core/blueprint/blueprint.rs b/src/core/blueprint/blueprint.rs index 8b3589dc06..89d2ea661a 100644 --- a/src/core/blueprint/blueprint.rs +++ b/src/core/blueprint/blueprint.rs @@ -8,6 +8,7 @@ use async_graphql::ValidationMode; use async_graphql_value::ConstValue; use derive_setters::Setters; use serde_json::Value; +use serde_json_borrow::OwnedValue; use super::telemetry::Telemetry; use super::GlobalTimeout; @@ -151,7 +152,7 @@ pub struct SchemaDefinition { pub struct InputFieldDefinition { pub name: String, pub of_type: Type, - pub default_value: Option, + pub default_value: Option, pub description: Option, } diff --git a/src/core/config/config.rs b/src/core/config/config.rs index d3dc6882cf..f1b438388d 100644 --- a/src/core/config/config.rs +++ b/src/core/config/config.rs @@ -7,6 +7,7 @@ use async_graphql::parser::types::ServiceDocument; use derive_setters::Setters; use serde::{Deserialize, Serialize}; use serde_json::Value; +use serde_json_borrow::OwnedValue; use super::telemetry::Telemetry; use super::{KeyValue, Link, Server, Upstream}; @@ -385,7 +386,7 @@ pub struct Arg { #[serde(default, skip_serializing_if = "is_default")] pub modify: Option, #[serde(default, skip_serializing_if = "is_default")] - pub default_value: Option, + pub default_value: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema, MergeRight)] diff --git a/src/core/config/from_document.rs b/src/core/config/from_document.rs index 01896659d2..60f95c974a 100644 --- a/src/core/config/from_document.rs +++ b/src/core/config/from_document.rs @@ -378,7 +378,10 @@ fn to_arg(input_value_definition: &InputValueDefinition) -> config::Arg { .flatten(); let default_value = if let Some(pos) = input_value_definition.default_value.as_ref() { let value = &pos.node; - serde_json::to_value(value).ok() + serde_json::to_string(value) + .map(|v| serde_json_borrow::OwnedValue::from_str(&v).ok()) + .ok() + .flatten() } else { None }; diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 62c51564ed..e1f4bbc89b 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -174,7 +174,7 @@ mod model { name: arg_name.clone(), type_of, value: Some(v.into_bvalue().into()), - default_value: None, // TODO: + default_value: arg.default_value.clone(), }; args.push(arg); } From 6815795278877a581e0076b079f8c7558290937d Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 11:42:44 +0530 Subject: [PATCH 06/20] remove unwraps --- src/core/ir/jit/mod.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index e1f4bbc89b..d612878764 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -134,9 +134,9 @@ mod model { document: ExecutableDocument, bpi: BlueprintIndex, query: &str, - ) -> Self { - let fields = convert_query_to_field1(document, &bpi, query).unwrap(); - Self { fields } + ) -> anyhow::Result { + let fields = convert_query_to_field(document, &bpi, query)?; + Ok(Self { fields }) } } @@ -147,7 +147,7 @@ mod model { id: &mut FieldId, arg_id: &mut ArgId, current_type: &str, - ) -> Vec> { + ) -> anyhow::Result>> { let mut fields = Vec::new(); for selection in selection_set.node.items { @@ -157,8 +157,8 @@ mod model { .node .arguments .into_iter() - .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const().unwrap())) - .collect::>(); + .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const())) + .collect::>>(); if let Some((_, fields_map)) = blueprint_index.map.get(current_type) { if let Some(field_def) = fields_map.get(field_name) { @@ -173,7 +173,13 @@ mod model { id, name: arg_name.clone(), type_of, - value: Some(v.into_bvalue().into()), + value: Some( + v.ok_or(anyhow::anyhow!( + "failed converting Const to Borrowed Value" + ))? + .into_bvalue() + .into(), + ), default_value: arg.default_value.clone(), }; args.push(arg); @@ -191,7 +197,7 @@ mod model { id, arg_id, type_of.name(), - )); + )?); let id = id.gen(); let field = Field { @@ -211,10 +217,10 @@ mod model { } } - fields + Ok(fields) } - fn convert_query_to_field1( + fn convert_query_to_field( document: ExecutableDocument, blueprint_index: &BlueprintIndex, query: &str, @@ -232,18 +238,18 @@ mod model { &mut id, &mut arg_id, query, - ); + )?; } DocumentOperations::Multiple(multiple) => { - multiple.into_iter().for_each(|(_, single)| { + for (_, single) in multiple { fields = resolve_selection_set( single.node.selection_set, blueprint_index, &mut id, &mut arg_id, query, - ); - }); + )?; + } } } From 701ad69a610fec0c1a3034099fb07097acfb179f Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 13:18:00 +0530 Subject: [PATCH 07/20] fix test --- ...__core__ir__jit__tests__from_document.snap | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/core/ir/jit/snapshots/tailcall__core__ir__jit__tests__from_document.snap b/src/core/ir/jit/snapshots/tailcall__core__ir__jit__tests__from_document.snap index 0ca8e91ff5..5b318a56ea 100644 --- a/src/core/ir/jit/snapshots/tailcall__core__ir__jit__tests__from_document.snap +++ b/src/core/ir/jit/snapshots/tailcall__core__ir__jit__tests__from_document.snap @@ -2,24 +2,26 @@ source: src/core/ir/jit/mod.rs expression: "format!(\"{:#?}\", q_blueprint)" --- -QueryBlueprint { - fields: [ - Field { - id: 0, - name: "id", - type_of: Int!, - }, - Field { - id: 1, - name: "user", - ir: "Some(..)", - type_of: User, - }, - Field { - id: 2, - name: "posts", - ir: "Some(..)", - type_of: [Post], - }, - ], -} +Ok( + QueryBlueprint { + fields: [ + Field { + id: 0, + name: "id", + type_of: Int!, + }, + Field { + id: 1, + name: "user", + ir: "Some(..)", + type_of: User, + }, + Field { + id: 2, + name: "posts", + ir: "Some(..)", + type_of: [Post], + }, + ], + }, +) From 472afcd6c4b8852e21c9a47d8d302527ebac6c1f Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 13:20:46 +0530 Subject: [PATCH 08/20] add fragment support --- src/core/ir/jit/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index d612878764..1de170e69f 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -230,6 +230,16 @@ mod model { let mut fields = Vec::new(); + for (_, fragment) in document.fragments { + fields = resolve_selection_set( + fragment.node.selection_set, + blueprint_index, + &mut id, + &mut arg_id, + query, + )?; + } + match document.operations { DocumentOperations::Single(single) => { fields = resolve_selection_set( From 1da699936978b2146fc8bb84819be50d74a2f8cf Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 13:41:29 +0530 Subject: [PATCH 09/20] fix build --- Cargo.lock | 1 + Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b0793a3b0..c46e6666d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4671,6 +4671,7 @@ dependencies = [ [[package]] name = "serde_json_borrow" version = "0.5.0" +source = "git+https://github.com/tailcallhq/serde_json_borrow?branch=temp#93f88f2e52b935b6e96161dfa2208fb584409ce4" dependencies = [ "schemars", "serde", diff --git a/Cargo.toml b/Cargo.toml index 1b8b41a950..10160f0c61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,8 +158,8 @@ datatest-stable = "0.2.9" tokio-test = "0.4.4" base64 = "0.22.1" tailcall-hasher = { path = "tailcall-hasher" } -#serde_json_borrow = {git = "https://github.com/tailcallhq/serde_json_borrow", branch = "temp"} -serde_json_borrow = {path = "../serde_json_borrow"} +serde_json_borrow = {git = "https://github.com/tailcallhq/serde_json_borrow", branch = "temp"} +#serde_json_borrow = {path = "../serde_json_borrow"} [dev-dependencies] tailcall-prettier = { path = "tailcall-prettier" } From 4378204ee32e9bc94b1d16f095b47fdffde714dc Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 3 Jun 2024 19:10:07 +0530 Subject: [PATCH 10/20] rename enum --- src/core/blueprint/blueprint_index.rs | 9 ++++----- src/core/ir/jit/mod.rs | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/blueprint/blueprint_index.rs index 55460e64e2..42f1f5fc5f 100644 --- a/src/core/blueprint/blueprint_index.rs +++ b/src/core/blueprint/blueprint_index.rs @@ -10,7 +10,7 @@ pub struct BlueprintIndex { #[derive(Debug)] pub enum FieldDef { Field(FieldDefinition), - InputFieldDefinition(InputFieldDefinition), + InputField(InputFieldDefinition), } impl BlueprintIndex { @@ -47,8 +47,7 @@ impl BlueprintIndex { let mut fields_map = HashMap::new(); for field in input_object_def.fields.clone() { - fields_map - .insert(field.name.clone(), FieldDef::InputFieldDefinition(field)); + fields_map.insert(field.name.clone(), FieldDef::InputField(field)); } map.insert( @@ -87,9 +86,9 @@ impl BlueprintIndex { self.map.get(type_name).map(|(definition, _)| definition) } - pub fn get_field(&self, query: &str, field_name: &str) -> Option<&FieldDef> { + pub fn get_field(&self, type_name: &str, field_name: &str) -> Option<&FieldDef> { self.map - .get(query) + .get(type_name) .and_then(|(_, fields_map)| fields_map.get(field_name)) } } diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 1de170e69f..8b566100c8 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -164,7 +164,7 @@ mod model { if let Some(field_def) = fields_map.get(field_name) { let mut args = vec![]; for (arg_name, v) in field_args { - if let Some(FieldDef::InputFieldDefinition(arg)) = + if let Some(FieldDef::InputField(arg)) = fields_map.get(&arg_name) { let type_of = arg.of_type.clone(); @@ -188,7 +188,7 @@ mod model { let type_of = match field_def { FieldDef::Field(field_def) => field_def.of_type.clone(), - FieldDef::InputFieldDefinition(field_def) => field_def.of_type.clone(), + FieldDef::InputField(field_def) => field_def.of_type.clone(), }; fields = fields.merge_right(resolve_selection_set( From e17ac0fd7dabf3b4b0b7a9c078977f22123e18c2 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 3 Jun 2024 19:22:36 +0530 Subject: [PATCH 11/20] add schema to blueprint-index --- src/core/blueprint/blueprint_index.rs | 52 ++++++++++--- src/core/ir/jit/mod.rs | 101 +++++++++++++------------- 2 files changed, 90 insertions(+), 63 deletions(-) diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/blueprint/blueprint_index.rs index 42f1f5fc5f..1a6dbb0eb6 100644 --- a/src/core/blueprint/blueprint_index.rs +++ b/src/core/blueprint/blueprint_index.rs @@ -1,10 +1,11 @@ use std::collections::HashMap; +use super::{Blueprint, SchemaDefinition}; use crate::core::blueprint::{Definition, FieldDefinition, InputFieldDefinition}; -#[derive(Debug)] pub struct BlueprintIndex { - pub map: HashMap)>, + map: HashMap)>, + schema: SchemaDefinition, } #[derive(Debug)] @@ -13,11 +14,23 @@ pub enum FieldDef { InputField(InputFieldDefinition), } +impl FieldDef { + pub fn get_arg(&self, arg_name: &str) -> Option<&InputFieldDefinition> { + match self { + FieldDef::Field(field) => { + // FIXME: use a hashmap to store the field args + field.args.iter().find(|arg| arg.name == arg_name) + } + FieldDef::InputField(_) => None, + } + } +} + impl BlueprintIndex { - pub fn init(definitions: Vec) -> Self { + pub fn init(blueprint: &Blueprint) -> Self { let mut map = HashMap::new(); - for definition in definitions { + for definition in blueprint.definitions.iter() { match definition { Definition::Object(object_def) => { let type_name = object_def.name.clone(); @@ -27,7 +40,10 @@ impl BlueprintIndex { fields_map.insert(field.name.clone(), FieldDef::Field(field.clone())); } - map.insert(type_name, (Definition::Object(object_def), fields_map)); + map.insert( + type_name, + (Definition::Object(object_def.to_owned()), fields_map), + ); } Definition::Interface(interface_def) => { let type_name = interface_def.name.clone(); @@ -39,7 +55,7 @@ impl BlueprintIndex { map.insert( type_name, - (Definition::Interface(interface_def), fields_map), + (Definition::Interface(interface_def.to_owned()), fields_map), ); } Definition::InputObject(input_object_def) => { @@ -52,34 +68,37 @@ impl BlueprintIndex { map.insert( type_name, - (Definition::InputObject(input_object_def), fields_map), + ( + Definition::InputObject(input_object_def.to_owned()), + fields_map, + ), ); } Definition::Scalar(scalar_def) => { let type_name = scalar_def.name.clone(); map.insert( type_name.clone(), - (Definition::Scalar(scalar_def), HashMap::new()), + (Definition::Scalar(scalar_def.to_owned()), HashMap::new()), ); } Definition::Enum(enum_def) => { let type_name = enum_def.name.clone(); map.insert( type_name.clone(), - (Definition::Enum(enum_def), HashMap::new()), + (Definition::Enum(enum_def.to_owned()), HashMap::new()), ); } Definition::Union(union_def) => { let type_name = union_def.name.clone(); map.insert( type_name.clone(), - (Definition::Union(union_def), HashMap::new()), + (Definition::Union(union_def.to_owned()), HashMap::new()), ); } } } - Self { map } + Self { map, schema: blueprint.schema.to_owned() } } pub fn get_type(&self, type_name: &str) -> Option<&Definition> { @@ -91,4 +110,15 @@ impl BlueprintIndex { .get(type_name) .and_then(|(_, fields_map)| fields_map.get(field_name)) } + + pub fn get_query_type(&self) -> Option<&Definition> { + self.get_type(&self.schema.query) + } + + pub fn get_mutation_type(&self) -> Option<&Definition> { + self.schema + .mutation + .as_ref() + .and_then(|a| self.get_type(&a)) + } } diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 8b566100c8..85856b76bb 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -13,6 +13,7 @@ mod model { use async_graphql::parser::types::{DocumentOperations, ExecutableDocument, Selection}; use async_graphql::Positioned; + use prost_reflect::prost_types::field; use serde_json_borrow::OwnedValue; use crate::core::blueprint::{ @@ -160,59 +161,55 @@ mod model { .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const())) .collect::>>(); - if let Some((_, fields_map)) = blueprint_index.map.get(current_type) { - if let Some(field_def) = fields_map.get(field_name) { - let mut args = vec![]; - for (arg_name, v) in field_args { - if let Some(FieldDef::InputField(arg)) = - fields_map.get(&arg_name) - { - let type_of = arg.of_type.clone(); - let id = arg_id.gen(); - let arg = Arg { - id, - name: arg_name.clone(), - type_of, - value: Some( - v.ok_or(anyhow::anyhow!( - "failed converting Const to Borrowed Value" - ))? - .into_bvalue() - .into(), - ), - default_value: arg.default_value.clone(), - }; - args.push(arg); - } + if let Some(field_def) = blueprint_index.get_field(current_type, field_name) { + let mut args = vec![]; + for (arg_name, v) in field_args { + if let Some(arg) = field_def.get_arg(&arg_name) { + let type_of = arg.of_type.clone(); + let id = arg_id.gen(); + let arg = Arg { + id, + name: arg_name.clone(), + type_of, + value: Some( + v.ok_or(anyhow::anyhow!( + "failed converting Const to Borrowed Value" + ))? + .into_bvalue() + .into(), + ), + default_value: arg.default_value.clone(), + }; + args.push(arg); } - - let type_of = match field_def { - FieldDef::Field(field_def) => field_def.of_type.clone(), - FieldDef::InputField(field_def) => field_def.of_type.clone(), - }; - - fields = fields.merge_right(resolve_selection_set( - gql_field.node.selection_set.clone(), - blueprint_index, - id, - arg_id, - type_of.name(), - )?); - - let id = id.gen(); - let field = Field { - id, - name: field_name.to_string(), - ir: match field_def { - FieldDef::Field(field_def) => field_def.resolver.clone(), - _ => None, - }, - type_of, - args, - refs: None, - }; - fields.push(field); } + + let type_of = match field_def { + FieldDef::Field(field_def) => field_def.of_type.clone(), + FieldDef::InputField(field_def) => field_def.of_type.clone(), + }; + + fields = fields.merge_right(resolve_selection_set( + gql_field.node.selection_set.clone(), + blueprint_index, + id, + arg_id, + type_of.name(), + )?); + + let id = id.gen(); + let field = Field { + id, + name: field_name.to_string(), + ir: match field_def { + FieldDef::Field(field_def) => field_def.resolver.clone(), + _ => None, + }, + type_of, + args, + refs: None, + }; + fields.push(field); } } } @@ -322,7 +319,7 @@ mod tests { } "#; let document = async_graphql::parser::parse_query(query).unwrap(); - let bp_index = BlueprintIndex::init(blueprint.definitions.clone()); + let bp_index = BlueprintIndex::init(&blueprint); let q_blueprint = model::QueryBlueprint::from_document(document, bp_index, "Query"); insta::assert_snapshot!(format!("{:#?}", q_blueprint)); } From 2f7c0eb79e598a1fde9f22aa163c0bfdf1139df8 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 3 Jun 2024 19:28:53 +0530 Subject: [PATCH 12/20] use blueprint-index to create query-plan --- src/core/blueprint/blueprint_index.rs | 13 +++++++++---- src/core/ir/jit/mod.rs | 7 +++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/blueprint/blueprint_index.rs index 1a6dbb0eb6..b2d66365bf 100644 --- a/src/core/blueprint/blueprint_index.rs +++ b/src/core/blueprint/blueprint_index.rs @@ -115,10 +115,15 @@ impl BlueprintIndex { self.get_type(&self.schema.query) } + pub fn get_query(&self) -> &String { + &self.schema.query + } + pub fn get_mutation_type(&self) -> Option<&Definition> { - self.schema - .mutation - .as_ref() - .and_then(|a| self.get_type(&a)) + self.schema.mutation.as_ref().and_then(|a| self.get_type(a)) + } + + pub fn get_mutation_type_name(&self) -> Option<&String> { + self.schema.mutation.as_ref() } } diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 85856b76bb..41d3fbaebc 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -134,9 +134,8 @@ mod model { pub fn from_document( document: ExecutableDocument, bpi: BlueprintIndex, - query: &str, ) -> anyhow::Result { - let fields = convert_query_to_field(document, &bpi, query)?; + let fields = convert_query_to_field(document, &bpi)?; Ok(Self { fields }) } } @@ -220,8 +219,8 @@ mod model { fn convert_query_to_field( document: ExecutableDocument, blueprint_index: &BlueprintIndex, - query: &str, ) -> anyhow::Result>> { + let query = blueprint_index.get_query(); let mut id = FieldId::new(0); let mut arg_id = ArgId::new(0); @@ -320,7 +319,7 @@ mod tests { "#; let document = async_graphql::parser::parse_query(query).unwrap(); let bp_index = BlueprintIndex::init(&blueprint); - let q_blueprint = model::QueryBlueprint::from_document(document, bp_index, "Query"); + let q_blueprint = model::QueryBlueprint::from_document(document, bp_index); insta::assert_snapshot!(format!("{:#?}", q_blueprint)); } } From ebd15adb8a4a4dcfb8e88b17d074f901b853d654 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 3 Jun 2024 19:29:39 +0530 Subject: [PATCH 13/20] rename query blueprint to query plan --- src/core/ir/jit/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 41d3fbaebc..484f74beba 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -126,11 +126,11 @@ mod model { pub struct Children(Vec); #[derive(Debug)] - pub struct QueryBlueprint { + pub struct QueryPlan { pub fields: Vec>, } - impl QueryBlueprint { + impl QueryPlan { pub fn from_document( document: ExecutableDocument, bpi: BlueprintIndex, @@ -319,7 +319,7 @@ mod tests { "#; let document = async_graphql::parser::parse_query(query).unwrap(); let bp_index = BlueprintIndex::init(&blueprint); - let q_blueprint = model::QueryBlueprint::from_document(document, bp_index); + let q_blueprint = model::QueryPlan::from_document(document, bp_index); insta::assert_snapshot!(format!("{:#?}", q_blueprint)); } } @@ -353,12 +353,12 @@ mod executor { use futures_util::future; use super::cache::Cache; - use super::model::{Field, FieldId, Parent, QueryBlueprint}; + use super::model::{Field, FieldId, Parent, QueryPlan}; use super::value::OwnedValue; use crate::core::ir::IR; pub struct ExecutionContext { - blueprint: QueryBlueprint, + plan: QueryPlan, cache: Cache, } @@ -380,7 +380,7 @@ mod executor { } fn find_field(&self, id: FieldId) -> Option<&Field> { - self.blueprint.fields.iter().find(|field| field.id == id) + self.plan.fields.iter().find(|field| field.id == id) } async fn execute_field( @@ -409,7 +409,7 @@ mod executor { } fn root(&self) -> Vec<&Field> { - self.blueprint + self.plan .fields .iter() .filter(|field| field.refs.is_none()) @@ -434,15 +434,15 @@ mod synth { pub use serde_json_borrow::*; use super::cache::Cache; - use super::model::QueryBlueprint; + use super::model::QueryPlan; struct Synth { - blueprint: QueryBlueprint, + blueprint: QueryPlan, cache: Cache, } impl Synth { - pub fn new(blueprint: QueryBlueprint) -> Self { + pub fn new(blueprint: QueryPlan) -> Self { Synth { blueprint, cache: Cache::empty() } } From e1c450ef723387c5d00f3df6dec942677fc82de9 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 19:32:46 +0530 Subject: [PATCH 14/20] args to hashmap --- src/core/blueprint/blueprint_index.rs | 27 +++++++++++++++---- src/core/ir/jit/mod.rs | 38 ++------------------------- 2 files changed, 24 insertions(+), 41 deletions(-) diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/blueprint/blueprint_index.rs index b2d66365bf..9d6697bbec 100644 --- a/src/core/blueprint/blueprint_index.rs +++ b/src/core/blueprint/blueprint_index.rs @@ -10,16 +10,16 @@ pub struct BlueprintIndex { #[derive(Debug)] pub enum FieldDef { - Field(FieldDefinition), + Field((FieldDefinition, HashMap)), InputField(InputFieldDefinition), } impl FieldDef { pub fn get_arg(&self, arg_name: &str) -> Option<&InputFieldDefinition> { match self { - FieldDef::Field(field) => { + FieldDef::Field((_, args)) => { // FIXME: use a hashmap to store the field args - field.args.iter().find(|arg| arg.name == arg_name) + args.get(arg_name) } FieldDef::InputField(_) => None, } @@ -37,7 +37,17 @@ impl BlueprintIndex { let mut fields_map = HashMap::new(); for field in &object_def.fields { - fields_map.insert(field.name.clone(), FieldDef::Field(field.clone())); + let args_map = HashMap::from_iter( + field + .args + .iter() + .map(|v| (v.name.clone(), v.clone())) + .collect::>(), + ); + fields_map.insert( + field.name.clone(), + FieldDef::Field((field.clone(), args_map)), + ); } map.insert( @@ -50,7 +60,14 @@ impl BlueprintIndex { let mut fields_map = HashMap::new(); for field in interface_def.fields.clone() { - fields_map.insert(field.name.clone(), FieldDef::Field(field)); + let args_map = HashMap::from_iter( + field + .args + .iter() + .map(|v| (v.name.clone(), v.clone())) + .collect::>(), + ); + fields_map.insert(field.name.clone(), FieldDef::Field((field, args_map))); } map.insert( diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 484f74beba..fb26a35cc8 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -184,7 +184,7 @@ mod model { } let type_of = match field_def { - FieldDef::Field(field_def) => field_def.of_type.clone(), + FieldDef::Field((field_def, _)) => field_def.of_type.clone(), FieldDef::InputField(field_def) => field_def.of_type.clone(), }; @@ -201,7 +201,7 @@ mod model { id, name: field_name.to_string(), ir: match field_def { - FieldDef::Field(field_def) => field_def.resolver.clone(), + FieldDef::Field((field_def, _)) => field_def.resolver.clone(), _ => None, }, type_of, @@ -261,40 +261,6 @@ mod model { Ok(fields) } - - fn find_definition<'a>( - name: &str, - definitions: &'a [Definition], - ) -> Option<&'a FieldDefinition> { - for def in definitions { - if let Definition::Object(object) = def { - for field in &object.fields { - if field.name == name { - return Some(field); - } - } - } - } - None - } - - fn find_definition_arg<'a>( - name: &str, - definitions: &'a [Definition], - ) -> Option<&'a InputFieldDefinition> { - for def in definitions { - if let Definition::Object(object) = def { - for field in &object.fields { - for arg in &field.args { - if arg.name == name { - return Some(arg); - } - } - } - } - } - None - } } #[cfg(test)] From e0465145ad79efc37f22bacebc691c1094c64e37 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 19:48:22 +0530 Subject: [PATCH 15/20] flag unused --- src/core/ir/jit/mod.rs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index fb26a35cc8..fa60197d2c 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -1,4 +1,3 @@ -#![allow(unused)] /// /// We need three executors for each query /// 1. Global general purpose executor (WE have this currently) @@ -13,20 +12,19 @@ mod model { use async_graphql::parser::types::{DocumentOperations, ExecutableDocument, Selection}; use async_graphql::Positioned; - use prost_reflect::prost_types::field; use serde_json_borrow::OwnedValue; - use crate::core::blueprint::{ - Blueprint, BlueprintIndex, Definition, FieldDef, FieldDefinition, InputFieldDefinition, - }; + use crate::core::blueprint::{BlueprintIndex, FieldDef}; use crate::core::ir::IR; use crate::core::merge_right::MergeRight; use crate::core::FromValue; + #[allow(unused)] trait IncrGen { fn gen(&mut self) -> Self; } + #[allow(unused)] #[derive(Debug)] pub enum Type { Named(String), @@ -34,6 +32,7 @@ mod model { Required(Box), } + #[allow(unused)] #[derive(Debug)] pub struct Arg { pub id: ArgId, @@ -59,16 +58,13 @@ mod model { } } + #[allow(unused)] impl ArgId { fn new(id: usize) -> Self { ArgId(id) } } - trait Id { - fn as_usize(&self) -> usize; - } - #[derive(Clone, PartialEq, Eq)] pub struct FieldId(usize); @@ -78,6 +74,7 @@ mod model { } } + #[allow(unused)] impl FieldId { fn new(id: usize) -> Self { FieldId(id) @@ -121,8 +118,10 @@ mod model { } #[derive(Debug)] + #[allow(unused)] pub struct Parent(FieldId); + #[allow(unused)] pub struct Children(Vec); #[derive(Debug)] @@ -130,6 +129,7 @@ mod model { pub fields: Vec>, } + #[allow(unused)] impl QueryPlan { pub fn from_document( document: ExecutableDocument, @@ -140,6 +140,7 @@ mod model { } } + #[allow(unused)] #[allow(clippy::too_many_arguments)] fn resolve_selection_set( selection_set: Positioned, @@ -216,6 +217,7 @@ mod model { Ok(fields) } + #[allow(unused)] fn convert_query_to_field( document: ExecutableDocument, blueprint_index: &BlueprintIndex, @@ -298,17 +300,23 @@ mod cache { use super::model::FieldId; use super::value::OwnedValue; + #[allow(unused)] pub struct Cache { map: Vec<(FieldId, OwnedValue)>, } + #[allow(unused)] impl Cache { + #[allow(unused)] pub fn empty() -> Self { Cache { map: Vec::new() } } + + #[allow(unused)] pub fn join(caches: Vec) -> Self { todo!() } + #[allow(unused)] pub fn get(&self, key: FieldId) -> Option<&OwnedValue> { todo!() } @@ -323,11 +331,12 @@ mod executor { use super::value::OwnedValue; use crate::core::ir::IR; + #[allow(unused)] pub struct ExecutionContext { plan: QueryPlan, cache: Cache, } - + #[allow(unused)] impl ExecutionContext { pub async fn execute_ir( &self, @@ -336,7 +345,6 @@ mod executor { ) -> anyhow::Result { todo!() } - fn find_children(&self, id: FieldId) -> Vec> { todo!() } @@ -406,7 +414,7 @@ mod synth { blueprint: QueryPlan, cache: Cache, } - + #[allow(unused)] impl Synth { pub fn new(blueprint: QueryPlan) -> Self { Synth { blueprint, cache: Cache::empty() } From 2ddb715c70fd677ecc8f6925c502a4ad314ea026 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 3 Jun 2024 19:47:04 +0530 Subject: [PATCH 16/20] update index for blueprint --- src/core/blueprint/blueprint_index.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/blueprint/blueprint_index.rs index 9d6697bbec..058d675801 100644 --- a/src/core/blueprint/blueprint_index.rs +++ b/src/core/blueprint/blueprint_index.rs @@ -3,6 +3,9 @@ use std::collections::HashMap; use super::{Blueprint, SchemaDefinition}; use crate::core::blueprint::{Definition, FieldDefinition, InputFieldDefinition}; +/// +/// A read optimized index for the blueprint. +/// pub struct BlueprintIndex { map: HashMap)>, schema: SchemaDefinition, From 342f1cd09360b2f7864abb82f25ab502addfc718 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 3 Jun 2024 20:03:02 +0530 Subject: [PATCH 17/20] add query plan builder --- src/core/blueprint/blueprint_index.rs | 1 - src/core/ir/jit/mod.rs | 230 +++++++++++++------------- 2 files changed, 117 insertions(+), 114 deletions(-) diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/blueprint/blueprint_index.rs index 058d675801..4727bd49fe 100644 --- a/src/core/blueprint/blueprint_index.rs +++ b/src/core/blueprint/blueprint_index.rs @@ -5,7 +5,6 @@ use crate::core::blueprint::{Definition, FieldDefinition, InputFieldDefinition}; /// /// A read optimized index for the blueprint. -/// pub struct BlueprintIndex { map: HashMap)>, schema: SchemaDefinition, diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index fa60197d2c..9bb36ffc57 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -14,7 +14,7 @@ mod model { use async_graphql::Positioned; use serde_json_borrow::OwnedValue; - use crate::core::blueprint::{BlueprintIndex, FieldDef}; + use crate::core::blueprint::{Blueprint, BlueprintIndex, FieldDef}; use crate::core::ir::IR; use crate::core::merge_right::MergeRight; use crate::core::FromValue; @@ -130,145 +130,147 @@ mod model { } #[allow(unused)] - impl QueryPlan { - pub fn from_document( - document: ExecutableDocument, - bpi: BlueprintIndex, - ) -> anyhow::Result { - let fields = convert_query_to_field(document, &bpi)?; - Ok(Self { fields }) - } + pub struct QueryPlanBuilder { + index: BlueprintIndex, } - #[allow(unused)] - #[allow(clippy::too_many_arguments)] - fn resolve_selection_set( - selection_set: Positioned, - blueprint_index: &BlueprintIndex, - id: &mut FieldId, - arg_id: &mut ArgId, - current_type: &str, - ) -> anyhow::Result>> { - let mut fields = Vec::new(); - - for selection in selection_set.node.items { - if let Selection::Field(gql_field) = selection.node { - let field_name = gql_field.node.name.node.as_str(); - let field_args = gql_field - .node - .arguments - .into_iter() - .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const())) - .collect::>>(); - - if let Some(field_def) = blueprint_index.get_field(current_type, field_name) { - let mut args = vec![]; - for (arg_name, v) in field_args { - if let Some(arg) = field_def.get_arg(&arg_name) { - let type_of = arg.of_type.clone(); - let id = arg_id.gen(); - let arg = Arg { - id, - name: arg_name.clone(), - type_of, - value: Some( - v.ok_or(anyhow::anyhow!( - "failed converting Const to Borrowed Value" - ))? - .into_bvalue() - .into(), - ), - default_value: arg.default_value.clone(), - }; - args.push(arg); + impl QueryPlanBuilder { + #[allow(unused)] + pub fn new(blueprint: Blueprint) -> Self { + let blueprint_index = BlueprintIndex::init(&blueprint); + Self { index: blueprint_index } + } + + #[allow(unused)] + pub fn build(&self, document: ExecutableDocument) -> anyhow::Result { + let fields = self.create_field_set(document)?; + Ok(QueryPlan { fields }) + } + + #[allow(clippy::too_many_arguments)] + fn resolve_selection_set( + &self, + selection_set: Positioned, + id: &mut FieldId, + arg_id: &mut ArgId, + current_type: &str, + ) -> anyhow::Result>> { + let mut fields = Vec::new(); + + for selection in selection_set.node.items { + if let Selection::Field(gql_field) = selection.node { + let field_name = gql_field.node.name.node.as_str(); + let field_args = gql_field + .node + .arguments + .into_iter() + .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const())) + .collect::>>(); + + if let Some(field_def) = self.index.get_field(current_type, field_name) { + let mut args = vec![]; + for (arg_name, v) in field_args { + if let Some(arg) = field_def.get_arg(&arg_name) { + let type_of = arg.of_type.clone(); + let id = arg_id.gen(); + let arg = Arg { + id, + name: arg_name.clone(), + type_of, + value: Some( + v.ok_or(anyhow::anyhow!( + "failed converting Const to Borrowed Value" + ))? + .into_bvalue() + .into(), + ), + default_value: arg.default_value.clone(), + }; + args.push(arg); + } } - } - let type_of = match field_def { - FieldDef::Field((field_def, _)) => field_def.of_type.clone(), - FieldDef::InputField(field_def) => field_def.of_type.clone(), - }; - - fields = fields.merge_right(resolve_selection_set( - gql_field.node.selection_set.clone(), - blueprint_index, - id, - arg_id, - type_of.name(), - )?); - - let id = id.gen(); - let field = Field { - id, - name: field_name.to_string(), - ir: match field_def { - FieldDef::Field((field_def, _)) => field_def.resolver.clone(), - _ => None, - }, - type_of, - args, - refs: None, - }; - fields.push(field); + let type_of = match field_def { + FieldDef::Field((field_def, _)) => field_def.of_type.clone(), + FieldDef::InputField(field_def) => field_def.of_type.clone(), + }; + + fields = fields.merge_right(self.resolve_selection_set( + gql_field.node.selection_set.clone(), + id, + arg_id, + type_of.name(), + )?); + + let id = id.gen(); + let field = Field { + id, + name: field_name.to_string(), + ir: match field_def { + FieldDef::Field((field_def, _)) => field_def.resolver.clone(), + _ => None, + }, + type_of, + args, + refs: None, + }; + fields.push(field); + } } } + + Ok(fields) } - Ok(fields) - } + fn create_field_set( + &self, + document: ExecutableDocument, + ) -> anyhow::Result>> { + let query = self.index.get_query(); + let mut id = FieldId::new(0); + let mut arg_id = ArgId::new(0); - #[allow(unused)] - fn convert_query_to_field( - document: ExecutableDocument, - blueprint_index: &BlueprintIndex, - ) -> anyhow::Result>> { - let query = blueprint_index.get_query(); - let mut id = FieldId::new(0); - let mut arg_id = ArgId::new(0); - - let mut fields = Vec::new(); - - for (_, fragment) in document.fragments { - fields = resolve_selection_set( - fragment.node.selection_set, - blueprint_index, - &mut id, - &mut arg_id, - query, - )?; - } + let mut fields = Vec::new(); - match document.operations { - DocumentOperations::Single(single) => { - fields = resolve_selection_set( - single.node.selection_set, - blueprint_index, + for (_, fragment) in document.fragments { + fields = self.resolve_selection_set( + fragment.node.selection_set, &mut id, &mut arg_id, query, )?; } - DocumentOperations::Multiple(multiple) => { - for (_, single) in multiple { - fields = resolve_selection_set( + + match document.operations { + DocumentOperations::Single(single) => { + fields = self.resolve_selection_set( single.node.selection_set, - blueprint_index, &mut id, &mut arg_id, query, )?; } + DocumentOperations::Multiple(multiple) => { + for (_, single) in multiple { + fields = self.resolve_selection_set( + single.node.selection_set, + &mut id, + &mut arg_id, + query, + )?; + } + } } - } - Ok(fields) + Ok(fields) + } } } #[cfg(test)] mod tests { use super::*; - use crate::core::blueprint::{Blueprint, BlueprintIndex}; + use crate::core::blueprint::Blueprint; use crate::core::config::reader::ConfigReader; #[tokio::test] @@ -286,8 +288,10 @@ mod tests { } "#; let document = async_graphql::parser::parse_query(query).unwrap(); - let bp_index = BlueprintIndex::init(&blueprint); - let q_blueprint = model::QueryPlan::from_document(document, bp_index); + + let q_blueprint = model::QueryPlanBuilder::new(blueprint) + .build(document) + .unwrap(); insta::assert_snapshot!(format!("{:#?}", q_blueprint)); } } From 41ac3c75a0bf4b09acfbb3e1a3788eb937b236bd Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 3 Jun 2024 20:15:36 +0530 Subject: [PATCH 18/20] update file names --- src/core/blueprint/mod.rs | 2 - src/core/generator/graphql_type.rs | 1 - .../jit/field_index.rs} | 47 +++++++------------ src/core/ir/jit/mod.rs | 15 +++--- src/core/rest/endpoint.rs | 2 +- 5 files changed, 26 insertions(+), 41 deletions(-) rename src/core/{blueprint/blueprint_index.rs => ir/jit/field_index.rs} (76%) diff --git a/src/core/blueprint/mod.rs b/src/core/blueprint/mod.rs index c9a65c026a..4af6452542 100644 --- a/src/core/blueprint/mod.rs +++ b/src/core/blueprint/mod.rs @@ -1,6 +1,5 @@ mod auth; mod blueprint; -mod blueprint_index; mod compress; mod cors; mod definitions; @@ -18,7 +17,6 @@ mod upstream; pub use auth::*; pub use blueprint::*; -pub use blueprint_index::*; pub use cors::*; pub use definitions::*; pub use dynamic_value::*; diff --git a/src/core/generator/graphql_type.rs b/src/core/generator/graphql_type.rs index 5835cbb70f..6dcd527368 100644 --- a/src/core/generator/graphql_type.rs +++ b/src/core/generator/graphql_type.rs @@ -132,7 +132,6 @@ impl GraphQLType { } } -// FIXME: make it private /// Used to convert proto type names to GraphQL formatted names. /// Enum to represent the type of the descriptor #[derive(Clone, Debug, PartialEq)] diff --git a/src/core/blueprint/blueprint_index.rs b/src/core/ir/jit/field_index.rs similarity index 76% rename from src/core/blueprint/blueprint_index.rs rename to src/core/ir/jit/field_index.rs index 4727bd49fe..191d2d2729 100644 --- a/src/core/blueprint/blueprint_index.rs +++ b/src/core/ir/jit/field_index.rs @@ -1,34 +1,33 @@ use std::collections::HashMap; -use super::{Blueprint, SchemaDefinition}; -use crate::core::blueprint::{Definition, FieldDefinition, InputFieldDefinition}; +use crate::core::blueprint::{ + Blueprint, Definition, FieldDefinition, InputFieldDefinition, SchemaDefinition, +}; /// -/// A read optimized index for the blueprint. -pub struct BlueprintIndex { - map: HashMap)>, +/// A read optimized index of all the fields in the Blueprint. Provide O(1) +/// access to getting any field information. +pub struct FieldIndex { + map: HashMap)>, schema: SchemaDefinition, } #[derive(Debug)] -pub enum FieldDef { +pub enum QueryField { Field((FieldDefinition, HashMap)), InputField(InputFieldDefinition), } -impl FieldDef { +impl QueryField { pub fn get_arg(&self, arg_name: &str) -> Option<&InputFieldDefinition> { match self { - FieldDef::Field((_, args)) => { - // FIXME: use a hashmap to store the field args - args.get(arg_name) - } - FieldDef::InputField(_) => None, + QueryField::Field((_, args)) => args.get(arg_name), + QueryField::InputField(_) => None, } } } -impl BlueprintIndex { +impl FieldIndex { pub fn init(blueprint: &Blueprint) -> Self { let mut map = HashMap::new(); @@ -48,7 +47,7 @@ impl BlueprintIndex { ); fields_map.insert( field.name.clone(), - FieldDef::Field((field.clone(), args_map)), + QueryField::Field((field.clone(), args_map)), ); } @@ -69,7 +68,7 @@ impl BlueprintIndex { .map(|v| (v.name.clone(), v.clone())) .collect::>(), ); - fields_map.insert(field.name.clone(), FieldDef::Field((field, args_map))); + fields_map.insert(field.name.clone(), QueryField::Field((field, args_map))); } map.insert( @@ -82,7 +81,7 @@ impl BlueprintIndex { let mut fields_map = HashMap::new(); for field in input_object_def.fields.clone() { - fields_map.insert(field.name.clone(), FieldDef::InputField(field)); + fields_map.insert(field.name.clone(), QueryField::InputField(field)); } map.insert( @@ -120,29 +119,17 @@ impl BlueprintIndex { Self { map, schema: blueprint.schema.to_owned() } } - pub fn get_type(&self, type_name: &str) -> Option<&Definition> { - self.map.get(type_name).map(|(definition, _)| definition) - } - - pub fn get_field(&self, type_name: &str, field_name: &str) -> Option<&FieldDef> { + pub fn get_field(&self, type_name: &str, field_name: &str) -> Option<&QueryField> { self.map .get(type_name) .and_then(|(_, fields_map)| fields_map.get(field_name)) } - pub fn get_query_type(&self) -> Option<&Definition> { - self.get_type(&self.schema.query) - } - pub fn get_query(&self) -> &String { &self.schema.query } - pub fn get_mutation_type(&self) -> Option<&Definition> { - self.schema.mutation.as_ref().and_then(|a| self.get_type(a)) - } - - pub fn get_mutation_type_name(&self) -> Option<&String> { + pub fn get_mutation(&self) -> Option<&String> { self.schema.mutation.as_ref() } } diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index 9bb36ffc57..edadeb43f1 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -5,7 +5,7 @@ /// 4. ?? which is working a bit level /// 5. Based on Data incoming and outgoing certain optimizations can further be /// made. - +mod field_index; mod model { use std::collections::HashMap; use std::fmt::{Debug, Formatter}; @@ -14,7 +14,8 @@ mod model { use async_graphql::Positioned; use serde_json_borrow::OwnedValue; - use crate::core::blueprint::{Blueprint, BlueprintIndex, FieldDef}; + use super::field_index::{FieldIndex, QueryField}; + use crate::core::blueprint::Blueprint; use crate::core::ir::IR; use crate::core::merge_right::MergeRight; use crate::core::FromValue; @@ -131,13 +132,13 @@ mod model { #[allow(unused)] pub struct QueryPlanBuilder { - index: BlueprintIndex, + index: FieldIndex, } impl QueryPlanBuilder { #[allow(unused)] pub fn new(blueprint: Blueprint) -> Self { - let blueprint_index = BlueprintIndex::init(&blueprint); + let blueprint_index = FieldIndex::init(&blueprint); Self { index: blueprint_index } } @@ -191,8 +192,8 @@ mod model { } let type_of = match field_def { - FieldDef::Field((field_def, _)) => field_def.of_type.clone(), - FieldDef::InputField(field_def) => field_def.of_type.clone(), + QueryField::Field((field_def, _)) => field_def.of_type.clone(), + QueryField::InputField(field_def) => field_def.of_type.clone(), }; fields = fields.merge_right(self.resolve_selection_set( @@ -207,7 +208,7 @@ mod model { id, name: field_name.to_string(), ir: match field_def { - FieldDef::Field((field_def, _)) => field_def.resolver.clone(), + QueryField::Field((field_def, _)) => field_def.resolver.clone(), _ => None, }, type_of, diff --git a/src/core/rest/endpoint.rs b/src/core/rest/endpoint.rs index eb1dfec0a4..448fbb5215 100644 --- a/src/core/rest/endpoint.rs +++ b/src/core/rest/endpoint.rs @@ -157,7 +157,7 @@ impl Endpoint { // Query let query = self.query_params.matches(query_params)?; - // FIXME: Too much cloning is happening via merge_variables + // TODO: Too much cloning is happening via merge_variables variables = merge_variables(variables, path); variables = merge_variables(variables, query); From f6bbf7ac7e074016fff7280410780d2270bb414c Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Mon, 3 Jun 2024 20:17:46 +0530 Subject: [PATCH 19/20] allow unused for get_mutation --- src/core/ir/jit/field_index.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/ir/jit/field_index.rs b/src/core/ir/jit/field_index.rs index 191d2d2729..bd64d9f2e9 100644 --- a/src/core/ir/jit/field_index.rs +++ b/src/core/ir/jit/field_index.rs @@ -129,6 +129,7 @@ impl FieldIndex { &self.schema.query } + #[allow(unused)] pub fn get_mutation(&self) -> Option<&String> { self.schema.mutation.as_ref() } From 7fc6926da571fe85eb1c7e276dc49345e9628e6d Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Mon, 3 Jun 2024 20:54:10 +0530 Subject: [PATCH 20/20] revert some unnecessary changes --- generated/.tailcallrc.schema.json | 14 +------- src/core/blueprint/blueprint.rs | 3 +- src/core/config/config.rs | 3 +- src/core/config/from_document.rs | 5 +-- src/core/helpers/value.rs | 17 ++++++++++ src/core/http/data_loader.rs | 7 +--- src/core/http/response.rs | 6 ++-- src/core/ir/jit/mod.rs | 25 ++++++--------- src/core/mod.rs | 53 +------------------------------ 9 files changed, 36 insertions(+), 97 deletions(-) diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index d243fbaf14..0fa6af467d 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -137,16 +137,7 @@ "type" ], "properties": { - "default_value": { - "anyOf": [ - { - "$ref": "#/definitions/OwnedValue" - }, - { - "type": "null" - } - ] - }, + "default_value": true, "doc": { "type": [ "string", @@ -889,9 +880,6 @@ } } }, - "OwnedValue": { - "type": "object" - }, "PhoneNumber": { "title": "PhoneNumber", "type": "object", diff --git a/src/core/blueprint/blueprint.rs b/src/core/blueprint/blueprint.rs index 89d2ea661a..8b3589dc06 100644 --- a/src/core/blueprint/blueprint.rs +++ b/src/core/blueprint/blueprint.rs @@ -8,7 +8,6 @@ use async_graphql::ValidationMode; use async_graphql_value::ConstValue; use derive_setters::Setters; use serde_json::Value; -use serde_json_borrow::OwnedValue; use super::telemetry::Telemetry; use super::GlobalTimeout; @@ -152,7 +151,7 @@ pub struct SchemaDefinition { pub struct InputFieldDefinition { pub name: String, pub of_type: Type, - pub default_value: Option, + pub default_value: Option, pub description: Option, } diff --git a/src/core/config/config.rs b/src/core/config/config.rs index f1b438388d..d3dc6882cf 100644 --- a/src/core/config/config.rs +++ b/src/core/config/config.rs @@ -7,7 +7,6 @@ use async_graphql::parser::types::ServiceDocument; use derive_setters::Setters; use serde::{Deserialize, Serialize}; use serde_json::Value; -use serde_json_borrow::OwnedValue; use super::telemetry::Telemetry; use super::{KeyValue, Link, Server, Upstream}; @@ -386,7 +385,7 @@ pub struct Arg { #[serde(default, skip_serializing_if = "is_default")] pub modify: Option, #[serde(default, skip_serializing_if = "is_default")] - pub default_value: Option, + pub default_value: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema, MergeRight)] diff --git a/src/core/config/from_document.rs b/src/core/config/from_document.rs index 60f95c974a..01896659d2 100644 --- a/src/core/config/from_document.rs +++ b/src/core/config/from_document.rs @@ -378,10 +378,7 @@ fn to_arg(input_value_definition: &InputValueDefinition) -> config::Arg { .flatten(); let default_value = if let Some(pos) = input_value_definition.default_value.as_ref() { let value = &pos.node; - serde_json::to_string(value) - .map(|v| serde_json_borrow::OwnedValue::from_str(&v).ok()) - .ok() - .flatten() + serde_json::to_value(value).ok() } else { None }; diff --git a/src/core/helpers/value.rs b/src/core/helpers/value.rs index 26a972932f..c80ac703ac 100644 --- a/src/core/helpers/value.rs +++ b/src/core/helpers/value.rs @@ -1,5 +1,6 @@ use std::hash::{Hash, Hasher}; +use async_graphql::Name; use async_graphql_value::ConstValue; #[derive(Clone, Eq)] @@ -36,3 +37,19 @@ pub fn hash(const_value: &ConstValue, state: &mut H) { } } } + +pub fn from_serde_owned(value: serde_json_borrow::Value) -> ConstValue { + use serde_json_borrow::Value; + match value { + Value::Null => ConstValue::Null, + Value::Bool(b) => ConstValue::Boolean(b), + Value::Number(n) => ConstValue::Number(n.into()), + Value::Str(s) => ConstValue::String(s.into()), + Value::Array(a) => ConstValue::List(a.into_iter().map(from_serde_owned).collect()), + Value::Object(o) => ConstValue::Object( + o.iter() + .map(|(k, v)| (Name::new(k), from_serde_owned(v.to_owned()))) + .collect(), + ), + } +} diff --git a/src/core/http/data_loader.rs b/src/core/http/data_loader.rs index 743929c8d8..6920fd6ba1 100644 --- a/src/core/http/data_loader.rs +++ b/src/core/http/data_loader.rs @@ -79,12 +79,7 @@ impl Loader for HttpDataLoader { first_url.query_pairs_mut().extend_pairs(url.query_pairs()); } - let res = self - .runtime - .http - .execute(request) - .await? - .to_json::()?; + let res = self.runtime.http.execute(request).await?.to_json()?; #[allow(clippy::mutable_key_type)] let mut hashmap = HashMap::with_capacity(keys.len()); let path = &group_by.path(); diff --git a/src/core/http/response.rs b/src/core/http/response.rs index c9971badc8..e719a654d5 100644 --- a/src/core/http/response.rs +++ b/src/core/http/response.rs @@ -8,8 +8,8 @@ use tonic::Status; use tonic_types::Status as GrpcStatus; use crate::core::grpc::protobuf::ProtobufOperation; +use crate::core::helpers::value::from_serde_owned; use crate::core::ir::EvaluationError; -use crate::core::FromValue; #[derive(Clone, Debug, Default, Setters)] pub struct Response { @@ -40,7 +40,7 @@ impl Response { } } - pub fn to_json(self) -> Result> { + pub fn to_json(self) -> Result> { if self.body.is_empty() { return Ok(Response { status: self.status, @@ -52,7 +52,7 @@ impl Response { // performance. Warning: Do not change this to direct conversion to `T` // without benchmarking the performance impact. let body: serde_json_borrow::Value = serde_json::from_slice(&self.body)?; - let body = T::from_value(body); + let body = from_serde_owned(body); Ok(Response { status: self.status, headers: self.headers, body }) } diff --git a/src/core/ir/jit/mod.rs b/src/core/ir/jit/mod.rs index edadeb43f1..7a8ccc4167 100644 --- a/src/core/ir/jit/mod.rs +++ b/src/core/ir/jit/mod.rs @@ -12,13 +12,11 @@ mod model { use async_graphql::parser::types::{DocumentOperations, ExecutableDocument, Selection}; use async_graphql::Positioned; - use serde_json_borrow::OwnedValue; use super::field_index::{FieldIndex, QueryField}; use crate::core::blueprint::Blueprint; use crate::core::ir::IR; use crate::core::merge_right::MergeRight; - use crate::core::FromValue; #[allow(unused)] trait IncrGen { @@ -39,8 +37,8 @@ mod model { pub id: ArgId, pub name: String, pub type_of: crate::core::blueprint::Type, - pub value: Option, - pub default_value: Option, + pub value: Option, + pub default_value: Option, } pub struct ArgId(usize); @@ -165,12 +163,12 @@ mod model { .node .arguments .into_iter() - .map(|(k, v)| (k.node.as_str().to_string(), v.node.into_const())) - .collect::>>(); + .map(|(k, v)| (k.node.as_str().to_string(), v.node)) + .collect::>(); if let Some(field_def) = self.index.get_field(current_type, field_name) { let mut args = vec![]; - for (arg_name, v) in field_args { + for (arg_name, value) in field_args { if let Some(arg) = field_def.get_arg(&arg_name) { let type_of = arg.of_type.clone(); let id = arg_id.gen(); @@ -178,14 +176,11 @@ mod model { id, name: arg_name.clone(), type_of, - value: Some( - v.ok_or(anyhow::anyhow!( - "failed converting Const to Borrowed Value" - ))? - .into_bvalue() - .into(), - ), - default_value: arg.default_value.clone(), + value: Some(value), + default_value: arg + .default_value + .as_ref() + .and_then(|v| v.to_owned().try_into().ok()), }; args.push(arg); } diff --git a/src/core/mod.rs b/src/core/mod.rs index a5ed499ddc..f715bc7c52 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -41,62 +41,11 @@ pub mod worker; use std::borrow::Cow; use std::hash::Hash; use std::num::NonZeroU64; -use std::str::FromStr; -use async_graphql_value::{ConstValue, Name}; +use async_graphql_value::ConstValue; use http::Response; pub use tailcall_macros as macros; -pub type BorrowedValue = serde_json_borrow::Value<'static>; - -pub trait FromValue { - fn from_value(value: serde_json_borrow::Value) -> Self; - fn into_bvalue(self) -> BorrowedValue; -} - -impl FromValue for async_graphql_value::ConstValue { - fn from_value(value: serde_json_borrow::Value) -> Self { - match value { - serde_json_borrow::Value::Null => ConstValue::Null, - serde_json_borrow::Value::Bool(b) => ConstValue::Boolean(b), - serde_json_borrow::Value::Number(n) => ConstValue::Number(n.into()), - serde_json_borrow::Value::Str(s) => ConstValue::String(s.into()), - serde_json_borrow::Value::Array(a) => { - ConstValue::List(a.into_iter().map(|v| Self::from_value(v)).collect()) - } - serde_json_borrow::Value::Object(o) => ConstValue::Object( - o.iter() - .map(|(k, v)| (Name::new(k), Self::from_value(v.to_owned()))) - .collect(), - ), - } - } - - fn into_bvalue(self) -> BorrowedValue { - match self { - async_graphql_value::ConstValue::Null => serde_json_borrow::Value::Null, - async_graphql_value::ConstValue::Boolean(b) => serde_json_borrow::Value::Bool(b), - async_graphql_value::ConstValue::Number(n) => serde_json_borrow::Value::Number( - serde_json_borrow::Number::from_str(&n.to_string()).unwrap(), - ), // TODO: FIXME - async_graphql_value::ConstValue::String(s) => { - serde_json_borrow::Value::Str(Cow::Owned(s)) - } - async_graphql_value::ConstValue::List(a) => serde_json_borrow::Value::Array( - a.into_iter().map(|v| v.into_bvalue()).collect::>(), - ), - async_graphql_value::ConstValue::Object(o) => serde_json_borrow::Value::Object( - o.into_iter() - .map(|(k, v)| (k.to_string(), v.into_bvalue())) - .collect::>() - .into(), - ), - async_graphql_value::ConstValue::Binary(_) => todo!(), - async_graphql_value::ConstValue::Enum(_) => todo!(), - } - } -} - pub trait EnvIO: Send + Sync + 'static { fn get(&self, key: &str) -> Option>; }