2021-04-04 04:05:54 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use futures_util::lock::Mutex;
|
|
|
|
|
2022-04-19 04:25:11 +00:00
|
|
|
use crate::{
|
|
|
|
extensions::{Extension, ExtensionContext, ExtensionFactory, NextRequest, NextValidation},
|
|
|
|
value, Response, ServerError, ValidationResult,
|
2021-04-05 04:21:02 +00:00
|
|
|
};
|
2020-12-18 15:58:03 +00:00
|
|
|
|
|
|
|
/// Analyzer extension
|
|
|
|
///
|
2022-04-19 04:25:11 +00:00
|
|
|
/// This extension will output the `analyzer` field containing `complexity` and
|
|
|
|
/// `depth` in the response extension of each query.
|
2020-12-18 15:58:03 +00:00
|
|
|
pub struct Analyzer;
|
|
|
|
|
|
|
|
impl ExtensionFactory for Analyzer {
|
2021-04-04 04:05:54 +00:00
|
|
|
fn create(&self) -> Arc<dyn Extension> {
|
|
|
|
Arc::new(AnalyzerExtension::default())
|
2020-12-18 15:58:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct AnalyzerExtension {
|
2021-04-04 04:05:54 +00:00
|
|
|
validation_result: Mutex<Option<ValidationResult>>,
|
2020-12-18 15:58:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 04:05:54 +00:00
|
|
|
#[async_trait::async_trait]
|
2020-12-18 15:58:03 +00:00
|
|
|
impl Extension for AnalyzerExtension {
|
2021-04-05 04:21:02 +00:00
|
|
|
async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Response {
|
|
|
|
let mut resp = next.run(ctx).await;
|
2021-04-04 04:05:54 +00:00
|
|
|
let validation_result = self.validation_result.lock().await.take();
|
|
|
|
if let Some(validation_result) = validation_result {
|
|
|
|
resp = resp.extension(
|
|
|
|
"analyzer",
|
|
|
|
value! ({
|
|
|
|
"complexity": validation_result.complexity,
|
|
|
|
"depth": validation_result.depth,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
resp
|
2020-12-18 15:58:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 04:05:54 +00:00
|
|
|
async fn validation(
|
|
|
|
&self,
|
|
|
|
ctx: &ExtensionContext<'_>,
|
2021-04-05 04:21:02 +00:00
|
|
|
next: NextValidation<'_>,
|
2021-04-04 04:05:54 +00:00
|
|
|
) -> Result<ValidationResult, Vec<ServerError>> {
|
2021-04-05 04:21:02 +00:00
|
|
|
let res = next.run(ctx).await?;
|
2021-04-04 04:05:54 +00:00
|
|
|
*self.validation_result.lock().await = Some(res);
|
|
|
|
Ok(res)
|
2020-12-18 15:58:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::*;
|
|
|
|
|
|
|
|
struct Query;
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
struct MyObj;
|
|
|
|
|
|
|
|
#[Object(internal)]
|
|
|
|
impl MyObj {
|
|
|
|
async fn value(&self) -> i32 {
|
|
|
|
1
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn obj(&self) -> MyObj {
|
|
|
|
MyObj
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[Object(internal)]
|
|
|
|
impl Query {
|
|
|
|
async fn value(&self) -> i32 {
|
|
|
|
1
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn obj(&self) -> MyObj {
|
|
|
|
MyObj
|
|
|
|
}
|
|
|
|
|
|
|
|
#[graphql(complexity = "count * child_complexity")]
|
|
|
|
async fn objs(&self, count: usize) -> Vec<MyObj> {
|
|
|
|
vec![MyObj; count as usize]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-12 04:47:24 +00:00
|
|
|
#[tokio::test]
|
2020-12-18 15:58:03 +00:00
|
|
|
async fn analyzer() {
|
|
|
|
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
|
|
|
|
.extension(extensions::Analyzer)
|
|
|
|
.finish();
|
|
|
|
|
2021-04-04 04:05:54 +00:00
|
|
|
let res = schema
|
2020-12-18 15:58:03 +00:00
|
|
|
.execute(
|
|
|
|
r#"{
|
|
|
|
value obj {
|
|
|
|
value obj {
|
|
|
|
value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
objs(count: 10) { value }
|
|
|
|
}"#,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.into_result()
|
|
|
|
.unwrap()
|
|
|
|
.extensions
|
2021-04-04 04:05:54 +00:00
|
|
|
.remove("analyzer");
|
2020-12-18 15:58:03 +00:00
|
|
|
assert_eq!(
|
2021-04-04 04:05:54 +00:00
|
|
|
res,
|
|
|
|
Some(value!({
|
|
|
|
"complexity": 5 + 10,
|
|
|
|
"depth": 3,
|
|
|
|
}))
|
2020-12-18 15:58:03 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|