Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: cache graphql query in query plan. #3106

Merged
merged 40 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8b40336
- render the selection set directly.
laststylebender14 Nov 13, 2024
11b7733
- store selection at graphql IO level.
laststylebender14 Nov 14, 2024
aa521de
- revert: selection from IO.
laststylebender14 Nov 14, 2024
a775b68
- store selection in request_template while generating the plan.
laststylebender14 Nov 14, 2024
e502f3e
- fix indentation
laststylebender14 Nov 14, 2024
2171fd7
- filter out args that has values.
laststylebender14 Nov 14, 2024
4ce9e4e
- skip IR fields.
laststylebender14 Nov 14, 2024
d66a424
impl JsonLike for Value.
laststylebender14 Nov 14, 2024
cea0573
- add support for directives.
laststylebender14 Nov 14, 2024
64e3dc2
- impl to_string_value for JsonLike
laststylebender14 Nov 14, 2024
fee9ea2
- we can apply GraphQL transformation only when selection set is enri…
laststylebender14 Nov 14, 2024
ccb918a
- add transformation to plan.
laststylebender14 Nov 14, 2024
44ddda1
- refactor: const_directive_to_sdl
laststylebender14 Nov 14, 2024
96df89e
- there's no point re-creating selection set during each request.
laststylebender14 Nov 14, 2024
186ab9f
- impl pathstring for Variables
laststylebender14 Nov 14, 2024
f8734dd
- compute the variables at runtime query with mustache
laststylebender14 Nov 14, 2024
8327582
- show query in info logger
laststylebender14 Nov 14, 2024
91db9ef
- handle modify directive scenario
laststylebender14 Nov 14, 2024
0b092e3
- lint changes
laststylebender14 Nov 14, 2024
3cd304c
- clean up of transform
laststylebender14 Nov 14, 2024
eae3162
- minor clean up
laststylebender14 Nov 14, 2024
c12de8b
- make pos inline
laststylebender14 Nov 14, 2024
8bcd4df
- clean up the selection API.
laststylebender14 Nov 14, 2024
57ee895
- rename func
laststylebender14 Nov 14, 2024
afd8ce4
- lint changes
laststylebender14 Nov 14, 2024
d70ddf7
- refactor: remove duplicate code.
laststylebender14 Nov 14, 2024
ab5ef48
- revert: change in eval context.
laststylebender14 Nov 14, 2024
600a769
- use to_string method instead
laststylebender14 Nov 14, 2024
a24351c
- test case fix: it's okay to send the directive to upstream.
laststylebender14 Nov 14, 2024
67ba766
Merge branch 'main' into fix/render-selection-set-directly
tusharmath Nov 18, 2024
d58e496
- add helper method modify_io, which allows user to modify io nodes.
laststylebender14 Nov 19, 2024
e6b7ee4
- resolve the variables in selection set of graphql query with modify_io
laststylebender14 Nov 19, 2024
f2ed3fb
- remove unused import
laststylebender14 Nov 19, 2024
e3c428a
- lint changes
laststylebender14 Nov 19, 2024
5c28ea1
Merge branch 'main' into fix/render-selection-set-directly
laststylebender14 Nov 19, 2024
a44d1b1
Merge branch 'main' into fix/render-selection-set-directly
laststylebender14 Nov 25, 2024
f46f4d1
- drop to_string_value method and use display instead.
laststylebender14 Nov 20, 2024
00bb2c3
- fix merge conflicts
laststylebender14 Nov 25, 2024
9953d08
Merge remote-tracking branch 'origin/main' into fix/render-selection-…
tusharmath Nov 28, 2024
b10fa32
Merge branch 'main' into fix/render-selection-set-directly
tusharmath Dec 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 79 additions & 43 deletions src/core/document.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::borrow::Cow;
use std::fmt::Display;

use async_graphql::parser::types::*;
use async_graphql::{Pos, Positioned};
use async_graphql_value::{ConstValue, Name};
use async_graphql::Positioned;
use async_graphql_value::ConstValue;

fn pos<A>(a: A) -> Positioned<A> {
Positioned::new(a, Pos::default())
}
use super::jit::Directive as JitDirective;
use super::json::JsonLikeOwned;

struct LineBreaker<'a> {
string: &'a str,
Expand Down Expand Up @@ -61,9 +63,12 @@
formatted_docs
}

pub fn print_directives<'a>(directives: impl Iterator<Item = &'a ConstDirective>) -> String {
pub fn print_directives<'a, T>(directives: impl Iterator<Item = &'a T>) -> String
where
&'a T: Into<Directive<'a>> + 'a,
{
directives
.map(|d| print_directive(&const_directive_to_sdl(d)))
.map(|d| print_directive(d))
.collect::<Vec<String>>()
.join(" ")
}
Expand Down Expand Up @@ -102,37 +107,6 @@
)
}

fn const_directive_to_sdl(directive: &ConstDirective) -> DirectiveDefinition {
DirectiveDefinition {
description: None,
name: pos(Name::new(directive.name.node.as_str())),
arguments: directive
.arguments
.iter()
.filter_map(|(k, v)| {
if v.node != ConstValue::Null {
Some(pos(InputValueDefinition {
description: None,
name: pos(Name::new(k.node.clone())),
ty: pos(Type {
nullable: true,
base: async_graphql::parser::types::BaseType::Named(Name::new(
v.to_string(),
)),
}),
default_value: Some(pos(ConstValue::String(v.to_string()))),
directives: Vec::new(),
}))
} else {
None
}
})
.collect(),
is_repeatable: true,
locations: vec![],
}
}

fn print_type_def(type_def: &TypeDefinition) -> String {
match &type_def.kind {
TypeKind::Scalar => {
Expand Down Expand Up @@ -320,18 +294,23 @@
print_default_value(field.default_value.as_ref())
)
}
fn print_directive(directive: &DirectiveDefinition) -> String {

pub fn print_directive<'a, T>(directive: &'a T) -> String
where
&'a T: Into<Directive<'a>>,
{
let directive: Directive<'a> = directive.into();
let args = directive
.arguments
.args
.iter()
.map(|arg| format!("{}: {}", arg.node.name.node, arg.node.ty.node))
.map(|arg| format!("{}: {}", arg.name, arg.value))
.collect::<Vec<String>>()
.join(", ");

if args.is_empty() {
format!("@{}", directive.name.node)
format!("@{}", directive.name)
} else {
format!("@{}({})", directive.name.node, args)
format!("@{}({})", directive.name, args)
}
}

Expand Down Expand Up @@ -420,3 +399,60 @@

sdl_string.trim_end_matches('\n').to_string()
}

pub struct Directive<'a> {
pub name: Cow<'a, str>,
pub args: Vec<Arg<'a>>,
}

pub struct Arg<'a> {
pub name: Cow<'a, str>,
pub value: Cow<'a, str>,
}

impl<'a> From<&'a ConstDirective> for Directive<'a> {
fn from(value: &'a ConstDirective) -> Self {
Self {
name: Cow::Borrowed(value.name.node.as_str()),
args: value
.arguments
.iter()
.filter_map(|(k, v)| {
if v.node != async_graphql_value::ConstValue::Null {
Some(Arg {
name: Cow::Borrowed(k.node.as_str()),
value: Cow::Owned(v.to_string()),
})
} else {
None
}
})
.collect(),
}
}
}

impl<'a, Input: JsonLikeOwned + Display> From<&'a JitDirective<Input>> for Directive<'a> {
fn from(value: &'a JitDirective<Input>) -> Self {
let to_mustache = |s: &str| -> String {
s.strip_prefix('$')
.map(|v| format!("{{{{{}}}}}", v))
.unwrap_or_else(|| s.to_string())
};
Self {
name: Cow::Borrowed(value.name.as_str()),
args: value
.arguments
.iter()
.filter_map(|(k, v)| {
if !v.is_null() {
let v_str = to_mustache(&v.to_string());
Some(Arg { name: Cow::Borrowed(k), value: Cow::Owned(v_str) })
} else {
None

Check warning on line 452 in src/core/document.rs

View check run for this annotation

Codecov / codecov/patch

src/core/document.rs#L452

Added line #L452 was not covered by tests
}
})
.collect(),
}
}
}
45 changes: 42 additions & 3 deletions src/core/graphql/request_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use derive_setters::Setters;
use http::header::{HeaderMap, HeaderValue};
use tailcall_hasher::TailcallHasher;
use tracing::info;

use crate::core::config::{GraphQLOperationType, KeyValue};
use crate::core::has_headers::HasHeaders;
Expand All @@ -14,7 +15,35 @@
use crate::core::ir::model::{CacheKey, IoId};
use crate::core::ir::{GraphQLOperationContext, RelatedFields};
use crate::core::mustache::Mustache;
use crate::core::path::PathGraphql;
use crate::core::path::{PathGraphql, PathString};

/// Represents a GraphQL selection that can either be resolved or unresolved.
#[derive(Debug, Clone)]
pub enum Selection {
/// A selection with a resolved string value.
Resolved(String),
/// A selection that contains a Mustache template to be resolved later.
UnResolved(Mustache),
}

impl Selection {
/// Resolves the `Unresolved` variant using the provided `PathString`.
pub fn resolve(self, p: &impl PathString) -> Selection {
match self {
Selection::UnResolved(template) => Selection::Resolved(template.render(p)),
resolved => resolved,
}
}
}

impl From<Mustache> for Selection {
fn from(value: Mustache) -> Self {
match value.is_const() {
true => Selection::Resolved(value.to_string()),
false => Selection::UnResolved(value),
}
}
}

/// RequestTemplate for GraphQL requests (See RequestTemplate documentation)
#[derive(Setters, Debug, Clone)]
Expand All @@ -26,6 +55,7 @@
pub operation_arguments: Option<Vec<(String, Mustache)>>,
pub headers: MustacheHeaders,
pub related_fields: RelatedFields,
pub selection: Option<Selection>,
}

impl RequestTemplate {
Expand Down Expand Up @@ -85,7 +115,12 @@
ctx: &C,
) -> String {
let operation_type = &self.operation_type;
let selection_set = ctx.selection_set(&self.related_fields).unwrap_or_default();

let selection_set = match &self.selection {
Some(Selection::Resolved(s)) => Cow::Borrowed(s),
Some(Selection::UnResolved(u)) => Cow::Owned(u.to_string()),

Check warning on line 121 in src/core/graphql/request_template.rs

View check run for this annotation

Codecov / codecov/patch

src/core/graphql/request_template.rs#L121

Added line #L121 was not covered by tests
None => Cow::Owned(ctx.selection_set(&self.related_fields).unwrap_or_default()),
};

let mut operation = Cow::Borrowed(&self.operation_name);

Expand Down Expand Up @@ -121,7 +156,10 @@
}
}

format!(r#"{{ "query": "{operation_type} {{ {operation} {selection_set} }}" }}"#)
let query =
format!(r#"{{ "query": "{operation_type} {{ {operation} {selection_set} }}" }}"#);
info!("Query {} ", query);
query
}

pub fn new(
Expand Down Expand Up @@ -149,6 +187,7 @@
operation_arguments,
headers,
related_fields,
selection: None,
})
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/core/ir/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,28 @@ impl Cache {
}

impl IR {
// allows to modify the IO node in the IR tree
pub fn modify_io(&mut self, io_modifier: &mut dyn FnMut(&mut IO)) {
match self {
IR::IO(io) => io_modifier(io),
IR::Cache(cache) => io_modifier(&mut cache.io),
IR::Discriminate(_, ir) | IR::Protect(_, ir) | IR::Path(ir, _) => {
ir.modify_io(io_modifier)
}
IR::Pipe(ir1, ir2) => {
ir1.modify_io(io_modifier);
ir2.modify_io(io_modifier);
}
IR::Entity(hash_map) => {
for ir in hash_map.values_mut() {
ir.modify_io(io_modifier);
}
}
IR::Map(map) => map.input.modify_io(io_modifier),
_ => {}
}
}

pub fn pipe(self, next: Self) -> Self {
IR::Pipe(Box::new(self), Box::new(next))
}
Expand Down
28 changes: 26 additions & 2 deletions src/core/jit/model.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::fmt::{Debug, Display, Formatter};
use std::num::NonZeroU64;
use std::sync::Arc;

Expand All @@ -13,12 +13,20 @@
use crate::core::blueprint::Index;
use crate::core::ir::model::IR;
use crate::core::ir::TypedValue;
use crate::core::json::JsonLike;
use crate::core::json::{JsonLike, JsonLikeOwned};
use crate::core::path::PathString;
use crate::core::scalar::Scalar;

#[derive(Debug, Deserialize, Clone)]
pub struct Variables<Value>(HashMap<String, Value>);

impl<V: JsonLikeOwned + Display> PathString for Variables<V> {
fn path_string<'a, T: AsRef<str>>(&'a self, path: &'a [T]) -> Option<Cow<'a, str>> {
self.get(path[0].as_ref())
.map(|v| Cow::Owned(v.to_string()))
}
}

impl<Value> Default for Variables<Value> {
fn default() -> Self {
Self::new()
Expand Down Expand Up @@ -96,6 +104,22 @@
pub default_value: Option<Input>,
}

impl<Input: Display> Display for Arg<Input> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let v = self
.value
.as_ref()
.map(|v| v.to_string())
.unwrap_or_else(|| {
self.default_value
.as_ref()
.map(|v| v.to_string())
.unwrap_or_default()

Check warning on line 117 in src/core/jit/model.rs

View check run for this annotation

Codecov / codecov/patch

src/core/jit/model.rs#L114-L117

Added lines #L114 - L117 were not covered by tests
});
write!(f, "{}: {}", self.name, v)
}
}

impl<Input> Arg<Input> {
pub fn try_map<Output, Error>(
self,
Expand Down
1 change: 1 addition & 0 deletions src/core/jit/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ impl Request<ConstValue> {
.pipe(transform::AuthPlanner::new())
.pipe(transform::CheckDedupe::new())
.pipe(transform::CheckCache::new())
.pipe(transform::GraphQL::new())
.transform(plan)
.to_result()
// both transformers are infallible right now
Expand Down
Loading
Loading