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(jit): support introspection in JIT #2769

Merged
merged 33 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
24af819
- execute introspection request with async_graphql
laststylebender14 Aug 29, 2024
cb587db
- remove std::mem::take
laststylebender14 Aug 29, 2024
5d5c9b3
- imple merge_right for async_gql response
laststylebender14 Aug 29, 2024
221a27b
Merge branch 'main' into fix/execute-introspection-requests-with-asyn…
laststylebender14 Aug 29, 2024
f7d1d0f
- add case where introspection is executed via async graphql and actu…
laststylebender14 Aug 29, 2024
d930e07
- lint formatting changes
laststylebender14 Aug 29, 2024
dd095f6
- drop clone
laststylebender14 Aug 29, 2024
c530b0b
- include birthdate in selection set
laststylebender14 Aug 29, 2024
e2fe5a2
- removed skip test marker
laststylebender14 Aug 29, 2024
3d908db
- utilise executable doc to figure out if query is of introspection.
laststylebender14 Aug 29, 2024
0285f69
- added case where introspection query present in selection set but i…
laststylebender14 Aug 29, 2024
278f179
- rename test
laststylebender14 Aug 29, 2024
7aa3b46
- deleting snaps: changed name of test
laststylebender14 Aug 29, 2024
295aad7
- skipping __typename as it's implemented in JIT
laststylebender14 Aug 29, 2024
ade7145
Merge branch 'main' into fix/execute-introspection-requests-with-asyn…
laststylebender14 Aug 29, 2024
358a20a
- added test for merging async responses
laststylebender14 Aug 29, 2024
da62b03
- added test for merging of errors
laststylebender14 Aug 29, 2024
ae11ff3
- lint changes
laststylebender14 Aug 29, 2024
28ad6ef
- added snap
laststylebender14 Aug 29, 2024
948d8ab
Merge branch 'main' into fix/execute-introspection-requests-with-asyn…
laststylebender14 Aug 29, 2024
2154995
- drop method, use make field public
laststylebender14 Aug 29, 2024
3b8f1b2
- revert: clone request outside.
laststylebender14 Aug 29, 2024
a9f5463
- compute is_introspection_query separately.
laststylebender14 Aug 29, 2024
080ea31
- lint changes
laststylebender14 Aug 29, 2024
f167043
- remove duplicate cloning
laststylebender14 Aug 29, 2024
0d86e00
Merge branch 'main' into fix/execute-introspection-requests-with-asyn…
laststylebender14 Aug 29, 2024
949df8c
inline function
tusharmath Aug 29, 2024
b6a6a26
- drop un-used method.
laststylebender14 Aug 29, 2024
4a2783e
- lint changes
laststylebender14 Aug 29, 2024
96c1969
use lift to implement clone
tusharmath Aug 29, 2024
c61b17c
- avoid cloning when unnecessary
laststylebender14 Aug 29, 2024
5eba36e
- allow dead code for lift
laststylebender14 Aug 29, 2024
5cf9433
Merge branch 'main' into fix/execute-introspection-requests-with-asyn…
tusharmath Aug 30, 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
17 changes: 16 additions & 1 deletion src/core/jit/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,22 @@
// skip the fields depending on variables.
fields.retain(|f| !f.skip(variables));

let plan = OperationPlan::new(fields, operation.ty, self.index.clone());
let is_introspection_query = operation.selection_set.node.items.iter().any(|f| {
if let Selection::Field(Positioned { node: gql_field, .. }) = &f.node {
let query = gql_field.name.node.as_str();
query.contains("__schema") || query.contains("__type")
} else {
false

Check warning on line 352 in src/core/jit/builder.rs

View check run for this annotation

Codecov / codecov/patch

src/core/jit/builder.rs#L352

Added line #L352 was not covered by tests
}
});

let plan = OperationPlan::new(
fields,
operation.ty,
self.index.clone(),
is_introspection_query,
tusharmath marked this conversation as resolved.
Show resolved Hide resolved
);

// TODO: operation from [ExecutableDocument] could contain definitions for
// default values of arguments. That info should be passed to
// [InputResolver] to resolve defaults properly
Expand Down
4 changes: 2 additions & 2 deletions src/core/jit/exec_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::core::jit::synth::Synth;

/// A specialized executor that executes with async_graphql::Value
pub struct ConstValueExecutor {
plan: OperationPlan<ConstValue>,
pub plan: OperationPlan<ConstValue>,
}

impl ConstValueExecutor {
Expand All @@ -24,7 +24,7 @@ impl ConstValueExecutor {
pub async fn execute(
self,
req_ctx: &RequestContext,
request: Request<ConstValue>,
request: &Request<ConstValue>,
) -> Response<ConstValue, Error> {
let exec = ConstValueExec::new(req_ctx);
let plan = self.plan;
Expand Down
44 changes: 39 additions & 5 deletions src/core/jit/graphql_executor.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::collections::BTreeMap;
use std::future::Future;
use std::sync::Arc;

use async_graphql::{Data, Executor, Response};
use async_graphql::{Data, Executor, Response, Value};
use futures_util::stream::BoxStream;

use crate::core::app_context::AppContext;
use crate::core::http::RequestContext;
use crate::core::jit;
use crate::core::jit::ConstValueExecutor;
use crate::core::merge_right::MergeRight;

#[derive(Clone)]
pub struct JITExecutor {
Expand All @@ -21,15 +23,47 @@
}
}

impl From<jit::Request<Value>> for async_graphql::Request {
fn from(value: jit::Request<Value>) -> Self {
let mut request = async_graphql::Request::new(value.query);
request.variables.extend(
value
.variables
.into_hashmap()
.into_iter()
.map(|(k, v)| (async_graphql::Name::new(k), v))
.collect::<BTreeMap<_, _>>(),
);
request.extensions = value.extensions;
request.operation_name = value.operation_name;
request
}

Check warning on line 40 in src/core/jit/graphql_executor.rs

View check run for this annotation

Codecov / codecov/patch

src/core/jit/graphql_executor.rs#L27-L40

Added lines #L27 - L40 were not covered by tests
}

impl Executor for JITExecutor {
fn execute(&self, request: async_graphql::Request) -> impl Future<Output = Response> + Send {
let request = jit::Request::from(request);
let jit_request = jit::Request::from(request);

async {
match ConstValueExecutor::new(&request, self.app_ctx.clone()) {
match ConstValueExecutor::new(&jit_request, self.app_ctx.clone()) {
Ok(exec) => {
let resp = exec.execute(&self.req_ctx, request).await;
resp.into_async_graphql()
let is_introspection_query =
self.app_ctx.blueprint.server.get_enable_introspection()
&& exec.plan.is_introspection_query;

let jit_resp = exec
.execute(&self.req_ctx, &jit_request)
.await

Check warning on line 56 in src/core/jit/graphql_executor.rs

View check run for this annotation

Codecov / codecov/patch

src/core/jit/graphql_executor.rs#L56

Added line #L56 was not covered by tests
.into_async_graphql();

if is_introspection_query {
let async_req =
async_graphql::Request::from(jit_request).only_introspection();
let async_resp = self.app_ctx.execute(async_req).await;
jit_resp.merge_right(async_resp)

Check warning on line 63 in src/core/jit/graphql_executor.rs

View check run for this annotation

Codecov / codecov/patch

src/core/jit/graphql_executor.rs#L60-L63

Added lines #L60 - L63 were not covered by tests
} else {
jit_resp
}
}
Err(error) => Response::from_errors(vec![error.into()]),
}
Expand Down
1 change: 1 addition & 0 deletions src/core/jit/input_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ where
new_fields,
self.plan.operation_type(),
self.plan.index.clone(),
self.plan.is_introspection_query,
))
}
}
15 changes: 14 additions & 1 deletion src/core/jit/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
pub fn get(&self, key: &str) -> Option<&Value> {
self.0.get(key)
}
pub fn into_hashmap(self) -> HashMap<String, Value> {
self.0
}

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

View check run for this annotation

Codecov / codecov/patch

src/core/jit/model.rs#L32-L34

Added lines #L32 - L34 were not covered by tests

pub fn insert(&mut self, key: String, value: Value) {
self.0.insert(key, value);
}
Expand Down Expand Up @@ -350,6 +354,7 @@
nested: Vec<Field<Nested<Input>, Input>>,
// TODO: drop index from here. Embed all the necessary information in each field of the plan.
pub index: Arc<Index>,
pub is_introspection_query: bool,
}

impl<Input> std::fmt::Debug for OperationPlan<Input> {
Expand Down Expand Up @@ -382,6 +387,7 @@
operation_type: self.operation_type,
nested,
index: self.index,
is_introspection_query: self.is_introspection_query,
})
}
}
Expand All @@ -391,6 +397,7 @@
fields: Vec<Field<Flat, Input>>,
operation_type: OperationType,
index: Arc<Index>,
is_introspection_query: bool,
) -> Self
where
Input: Clone,
Expand All @@ -402,7 +409,13 @@
.map(|f| f.into_nested(&fields))
.collect::<Vec<_>>();

Self { flat: fields, nested, operation_type, index }
Self {
flat: fields,
nested,
operation_type,
index,
is_introspection_query,
}
}

/// Returns a graphQL operation type
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 @@ -19,6 +19,7 @@ pub struct Request<V> {
pub extensions: HashMap<String, V>,
}

// NOTE: This is hot code and should allocate minimal memory
impl From<async_graphql::Request> for Request<ConstValue> {
fn from(mut value: async_graphql::Request) -> Self {
let variables = std::mem::take(value.variables.deref_mut());
Expand Down
82 changes: 82 additions & 0 deletions src/core/jit/response.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::borrow::BorrowMut;

use derive_setters::Setters;
use serde::Serialize;

use super::Positioned;
use crate::core::jit;
use crate::core::merge_right::MergeRight;

#[derive(Setters, Serialize)]
pub struct Response<Value, Error> {
Expand Down Expand Up @@ -31,6 +34,22 @@ impl<Value, Error> Response<Value, Error> {
}
}

impl MergeRight for async_graphql::Response {
fn merge_right(mut self, other: Self) -> Self {
if let async_graphql::Value::Object(mut other_obj) = other.data {
if let async_graphql::Value::Object(self_obj) = std::mem::take(self.data.borrow_mut()) {
other_obj.extend(self_obj);
self.data = async_graphql::Value::Object(other_obj);
}
}

self.errors.extend(other.errors);
self.extensions.extend(other.extensions);

self
}
}

impl Response<async_graphql::Value, jit::Error> {
pub fn into_async_graphql(self) -> async_graphql::Response {
let mut resp = async_graphql::Response::new(self.data.unwrap_or_default());
Expand All @@ -50,6 +69,7 @@ mod test {

use super::Response;
use crate::core::jit::{self, Pos, Positioned};
use crate::core::merge_right::MergeRight;

#[test]
fn test_with_response() {
Expand Down Expand Up @@ -117,4 +137,66 @@ mod test {
assert_eq!(async_response.errors.len(), 2);
insta::assert_debug_snapshot!(async_response);
}

#[test]
pub fn test_merging_of_responses() {
let introspection_response = r#"
{
"__type": {
"name": "User",
"fields": [
{
"name": "birthday",
"type": {
"name": "Date"
}
},
{
"name": "id",
"type": {
"name": "String"
}
}
]
}
}
"#;
let introspection_data =
ConstValue::from_json(serde_json::from_str(introspection_response).unwrap()).unwrap();
let introspection_response = async_graphql::Response::new(introspection_data);

let user_response = r#"
{
"me": {
"id": 1,
"name": "John Smith",
"birthday": "2023-03-08T12:45:26-05:00"
}
}
"#;
let user_data =
ConstValue::from_json(serde_json::from_str(user_response).unwrap()).unwrap();
let query_response = async_graphql::Response::new(user_data);

let merged_response = introspection_response.merge_right(query_response);

insta::assert_json_snapshot!(merged_response);
}

#[test]
pub fn test_merging_of_errors() {
let mut resp1 = async_graphql::Response::new(ConstValue::default());
let mut err1 = vec![async_graphql::ServerError::new("Error-1", None)];
resp1.errors.append(&mut err1);

let mut resp2 = async_graphql::Response::new(ConstValue::default());
let mut err2 = vec![async_graphql::ServerError::new(
"Error-2",
Some(async_graphql::Pos::default()),
)];
resp2.errors.append(&mut err2);

let merged_resp = resp1.merge_right(resp2);
insta::assert_json_snapshot!(merged_resp);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
source: src/core/jit/response.rs
expression: merged_response
---
{
"data": {
"me": {
"id": 1,
"name": "John Smith",
"birthday": "2023-03-08T12:45:26-05:00"
},
"__type": {
"name": "User",
"fields": [
{
"name": "birthday",
"type": {
"name": "Date"
}
},
{
"name": "id",
"type": {
"name": "String"
}
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
source: src/core/jit/response.rs
expression: merged_resp
---
{
"data": null,
"errors": [
{
"message": "Error-1"
},
{
"message": "Error-2",
"locations": [
{
"line": 0,
"column": 0
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
source: src/core/jit/response.rs
expression: merged_response
---
{
"data": {
"me": {
"id": 1,
"name": "John Smith",
"birthday": "2023-03-08T12:45:26-05:00"
},
"__type": {
"name": "User",
"fields": [
{
"name": "birthday",
"type": {
"name": "Date"
}
},
{
"name": "id",
"type": {
"name": "String"
}
}
]
}
}
}
Loading
Loading