WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 316254c

Browse files
feat: cache graphql query in query plan. (#3106)
Co-authored-by: Tushar Mathur <[email protected]>
1 parent 0b05b83 commit 316254c

File tree

14 files changed

+615
-52
lines changed

14 files changed

+615
-52
lines changed

src/core/document.rs

Lines changed: 79 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use std::borrow::Cow;
2+
use std::fmt::Display;
3+
14
use async_graphql::parser::types::*;
2-
use async_graphql::{Pos, Positioned};
3-
use async_graphql_value::{ConstValue, Name};
5+
use async_graphql::Positioned;
6+
use async_graphql_value::ConstValue;
47

5-
fn pos<A>(a: A) -> Positioned<A> {
6-
Positioned::new(a, Pos::default())
7-
}
8+
use super::jit::Directive as JitDirective;
9+
use super::json::JsonLikeOwned;
810

911
struct LineBreaker<'a> {
1012
string: &'a str,
@@ -61,9 +63,12 @@ fn get_formatted_docs(docs: Option<String>, indent: usize) -> String {
6163
formatted_docs
6264
}
6365

64-
pub fn print_directives<'a>(directives: impl Iterator<Item = &'a ConstDirective>) -> String {
66+
pub fn print_directives<'a, T>(directives: impl Iterator<Item = &'a T>) -> String
67+
where
68+
&'a T: Into<Directive<'a>> + 'a,
69+
{
6570
directives
66-
.map(|d| print_directive(&const_directive_to_sdl(d)))
71+
.map(|d| print_directive(d))
6772
.collect::<Vec<String>>()
6873
.join(" ")
6974
}
@@ -102,37 +107,6 @@ fn print_schema(schema: &SchemaDefinition) -> String {
102107
)
103108
}
104109

105-
fn const_directive_to_sdl(directive: &ConstDirective) -> DirectiveDefinition {
106-
DirectiveDefinition {
107-
description: None,
108-
name: pos(Name::new(directive.name.node.as_str())),
109-
arguments: directive
110-
.arguments
111-
.iter()
112-
.filter_map(|(k, v)| {
113-
if v.node != ConstValue::Null {
114-
Some(pos(InputValueDefinition {
115-
description: None,
116-
name: pos(Name::new(k.node.clone())),
117-
ty: pos(Type {
118-
nullable: true,
119-
base: async_graphql::parser::types::BaseType::Named(Name::new(
120-
v.to_string(),
121-
)),
122-
}),
123-
default_value: Some(pos(ConstValue::String(v.to_string()))),
124-
directives: Vec::new(),
125-
}))
126-
} else {
127-
None
128-
}
129-
})
130-
.collect(),
131-
is_repeatable: true,
132-
locations: vec![],
133-
}
134-
}
135-
136110
fn print_type_def(type_def: &TypeDefinition) -> String {
137111
match &type_def.kind {
138112
TypeKind::Scalar => {
@@ -320,18 +294,23 @@ fn print_input_value(field: &async_graphql::parser::types::InputValueDefinition)
320294
print_default_value(field.default_value.as_ref())
321295
)
322296
}
323-
fn print_directive(directive: &DirectiveDefinition) -> String {
297+
298+
pub fn print_directive<'a, T>(directive: &'a T) -> String
299+
where
300+
&'a T: Into<Directive<'a>>,
301+
{
302+
let directive: Directive<'a> = directive.into();
324303
let args = directive
325-
.arguments
304+
.args
326305
.iter()
327-
.map(|arg| format!("{}: {}", arg.node.name.node, arg.node.ty.node))
306+
.map(|arg| format!("{}: {}", arg.name, arg.value))
328307
.collect::<Vec<String>>()
329308
.join(", ");
330309

331310
if args.is_empty() {
332-
format!("@{}", directive.name.node)
311+
format!("@{}", directive.name)
333312
} else {
334-
format!("@{}({})", directive.name.node, args)
313+
format!("@{}({})", directive.name, args)
335314
}
336315
}
337316

@@ -420,3 +399,60 @@ pub fn print(sd: ServiceDocument) -> String {
420399

421400
sdl_string.trim_end_matches('\n').to_string()
422401
}
402+
403+
pub struct Directive<'a> {
404+
pub name: Cow<'a, str>,
405+
pub args: Vec<Arg<'a>>,
406+
}
407+
408+
pub struct Arg<'a> {
409+
pub name: Cow<'a, str>,
410+
pub value: Cow<'a, str>,
411+
}
412+
413+
impl<'a> From<&'a ConstDirective> for Directive<'a> {
414+
fn from(value: &'a ConstDirective) -> Self {
415+
Self {
416+
name: Cow::Borrowed(value.name.node.as_str()),
417+
args: value
418+
.arguments
419+
.iter()
420+
.filter_map(|(k, v)| {
421+
if v.node != async_graphql_value::ConstValue::Null {
422+
Some(Arg {
423+
name: Cow::Borrowed(k.node.as_str()),
424+
value: Cow::Owned(v.to_string()),
425+
})
426+
} else {
427+
None
428+
}
429+
})
430+
.collect(),
431+
}
432+
}
433+
}
434+
435+
impl<'a, Input: JsonLikeOwned + Display> From<&'a JitDirective<Input>> for Directive<'a> {
436+
fn from(value: &'a JitDirective<Input>) -> Self {
437+
let to_mustache = |s: &str| -> String {
438+
s.strip_prefix('$')
439+
.map(|v| format!("{{{{{}}}}}", v))
440+
.unwrap_or_else(|| s.to_string())
441+
};
442+
Self {
443+
name: Cow::Borrowed(value.name.as_str()),
444+
args: value
445+
.arguments
446+
.iter()
447+
.filter_map(|(k, v)| {
448+
if !v.is_null() {
449+
let v_str = to_mustache(&v.to_string());
450+
Some(Arg { name: Cow::Borrowed(k), value: Cow::Owned(v_str) })
451+
} else {
452+
None
453+
}
454+
})
455+
.collect(),
456+
}
457+
}
458+
}

src/core/graphql/request_template.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::hash::{Hash, Hasher};
66
use derive_setters::Setters;
77
use http::header::{HeaderMap, HeaderValue};
88
use tailcall_hasher::TailcallHasher;
9+
use tracing::info;
910

1011
use crate::core::config::{GraphQLOperationType, KeyValue};
1112
use crate::core::has_headers::HasHeaders;
@@ -14,7 +15,35 @@ use crate::core::http::Method::POST;
1415
use crate::core::ir::model::{CacheKey, IoId};
1516
use crate::core::ir::{GraphQLOperationContext, RelatedFields};
1617
use crate::core::mustache::Mustache;
17-
use crate::core::path::PathGraphql;
18+
use crate::core::path::{PathGraphql, PathString};
19+
20+
/// Represents a GraphQL selection that can either be resolved or unresolved.
21+
#[derive(Debug, Clone)]
22+
pub enum Selection {
23+
/// A selection with a resolved string value.
24+
Resolved(String),
25+
/// A selection that contains a Mustache template to be resolved later.
26+
UnResolved(Mustache),
27+
}
28+
29+
impl Selection {
30+
/// Resolves the `Unresolved` variant using the provided `PathString`.
31+
pub fn resolve(self, p: &impl PathString) -> Selection {
32+
match self {
33+
Selection::UnResolved(template) => Selection::Resolved(template.render(p)),
34+
resolved => resolved,
35+
}
36+
}
37+
}
38+
39+
impl From<Mustache> for Selection {
40+
fn from(value: Mustache) -> Self {
41+
match value.is_const() {
42+
true => Selection::Resolved(value.to_string()),
43+
false => Selection::UnResolved(value),
44+
}
45+
}
46+
}
1847

1948
/// RequestTemplate for GraphQL requests (See RequestTemplate documentation)
2049
#[derive(Setters, Debug, Clone)]
@@ -26,6 +55,7 @@ pub struct RequestTemplate {
2655
pub operation_arguments: Option<Vec<(String, Mustache)>>,
2756
pub headers: MustacheHeaders,
2857
pub related_fields: RelatedFields,
58+
pub selection: Option<Selection>,
2959
}
3060

3161
impl RequestTemplate {
@@ -85,7 +115,12 @@ impl RequestTemplate {
85115
ctx: &C,
86116
) -> String {
87117
let operation_type = &self.operation_type;
88-
let selection_set = ctx.selection_set(&self.related_fields).unwrap_or_default();
118+
119+
let selection_set = match &self.selection {
120+
Some(Selection::Resolved(s)) => Cow::Borrowed(s),
121+
Some(Selection::UnResolved(u)) => Cow::Owned(u.to_string()),
122+
None => Cow::Owned(ctx.selection_set(&self.related_fields).unwrap_or_default()),
123+
};
89124

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

@@ -121,7 +156,10 @@ impl RequestTemplate {
121156
}
122157
}
123158

124-
format!(r#"{{ "query": "{operation_type} {{ {operation} {selection_set} }}" }}"#)
159+
let query =
160+
format!(r#"{{ "query": "{operation_type} {{ {operation} {selection_set} }}" }}"#);
161+
info!("Query {} ", query);
162+
query
125163
}
126164

127165
pub fn new(
@@ -149,6 +187,7 @@ impl RequestTemplate {
149187
operation_arguments,
150188
headers,
151189
related_fields,
190+
selection: None,
152191
})
153192
}
154193
}

src/core/ir/model.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,28 @@ impl Cache {
131131
}
132132

133133
impl IR {
134+
// allows to modify the IO node in the IR tree
135+
pub fn modify_io(&mut self, io_modifier: &mut dyn FnMut(&mut IO)) {
136+
match self {
137+
IR::IO(io) => io_modifier(io),
138+
IR::Cache(cache) => io_modifier(&mut cache.io),
139+
IR::Discriminate(_, ir) | IR::Protect(_, ir) | IR::Path(ir, _) => {
140+
ir.modify_io(io_modifier)
141+
}
142+
IR::Pipe(ir1, ir2) => {
143+
ir1.modify_io(io_modifier);
144+
ir2.modify_io(io_modifier);
145+
}
146+
IR::Entity(hash_map) => {
147+
for ir in hash_map.values_mut() {
148+
ir.modify_io(io_modifier);
149+
}
150+
}
151+
IR::Map(map) => map.input.modify_io(io_modifier),
152+
_ => {}
153+
}
154+
}
155+
134156
pub fn pipe(self, next: Self) -> Self {
135157
IR::Pipe(Box::new(self), Box::new(next))
136158
}

src/core/jit/model.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::borrow::Cow;
22
use std::collections::HashMap;
3-
use std::fmt::{Debug, Formatter};
3+
use std::fmt::{Debug, Display, Formatter};
44
use std::num::NonZeroU64;
55
use std::sync::Arc;
66

@@ -13,12 +13,20 @@ use super::Error;
1313
use crate::core::blueprint::Index;
1414
use crate::core::ir::model::IR;
1515
use crate::core::ir::TypedValue;
16-
use crate::core::json::JsonLike;
16+
use crate::core::json::{JsonLike, JsonLikeOwned};
17+
use crate::core::path::PathString;
1718
use crate::core::scalar::Scalar;
1819

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

23+
impl<V: JsonLikeOwned + Display> PathString for Variables<V> {
24+
fn path_string<'a, T: AsRef<str>>(&'a self, path: &'a [T]) -> Option<Cow<'a, str>> {
25+
self.get(path[0].as_ref())
26+
.map(|v| Cow::Owned(v.to_string()))
27+
}
28+
}
29+
2230
impl<Value> Default for Variables<Value> {
2331
fn default() -> Self {
2432
Self::new()
@@ -96,6 +104,22 @@ pub struct Arg<Input> {
96104
pub default_value: Option<Input>,
97105
}
98106

107+
impl<Input: Display> Display for Arg<Input> {
108+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
109+
let v = self
110+
.value
111+
.as_ref()
112+
.map(|v| v.to_string())
113+
.unwrap_or_else(|| {
114+
self.default_value
115+
.as_ref()
116+
.map(|v| v.to_string())
117+
.unwrap_or_default()
118+
});
119+
write!(f, "{}: {}", self.name, v)
120+
}
121+
}
122+
99123
impl<Input> Arg<Input> {
100124
pub fn try_map<Output, Error>(
101125
self,

src/core/jit/request.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ impl Request<ConstValue> {
5050
.pipe(transform::AuthPlanner::new())
5151
.pipe(transform::CheckDedupe::new())
5252
.pipe(transform::CheckCache::new())
53+
.pipe(transform::GraphQL::new())
5354
.transform(plan)
5455
.to_result()
5556
// both transformers are infallible right now

0 commit comments

Comments
 (0)