🙌

async-graphql で ValidationResult (complexity, depth) を確認する

に公開

async-graphql の limit_complexity と limit_depth

async-graphql crate (7.0.17) には query の complexity と depth の制限を設定する機能があります。

ドキュメントでは↓に記載されています。

https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html

具体的なメソッドとしては limit_complexitylimit_depth です。

どのような値を設定すればいいか

これらのメソッドで制限を設定することはできますが、ここにどのような値を設定すべきかはすこし迷うところです。

こういうときは実際に使われている値を確認してみると良さそうです。

この記事では query の complexity と depth を確認する方法について書いてみます。

方法 1: analyzer extension を使う

async-graphql には analyzer extension があります。

https://docs.rs/async-graphql/7.0.17/async_graphql/extensions/struct.Analyzer.html

この extension はレスポンスの extensionsanalyzercomplexitydepth を含むオブジェクトを設定します。

{"data":{"int":123},"extensions":{"complexity":1,"depth":1}}

今回のケースでは使われている値なのかを確認したいだけなので、レスポンスからは削っておくと良いです。

async_graphql::Response::extensions から "analyzer" を削ると良いです。

https://github.com/bouzuya/rust-examples/blob/1aa40081b65db1aa969ea29797463983fc67d298/async-graphql6/src/main.rs#L32-L56

async fn test_graphql_request(
    request: impl Into<async_graphql::Request>,
    expected_response: &str,
    expected_analyzer_response: &str,
) -> anyhow::Result<()> {
    let schema = async_graphql::Schema::build(
        MyObject,
        async_graphql::EmptyMutation,
        async_graphql::EmptySubscription,
    )
    .extension(async_graphql::extensions::Analyzer)
    .finish();
    let request: async_graphql::Request = request.into();
    let mut response: async_graphql::Response = schema.execute(request).await;
    let analyzer_response = response.extensions.remove("analyzer");
    assert_eq!(
        serde_json::Value::from_str(&serde_json::to_string(&response)?)?,
        serde_json::Value::from_str(&expected_response)?
    );
    assert_eq!(
        serde_json::Value::from_str(&serde_json::to_string(&analyzer_response)?)?,
        serde_json::Value::from_str(&expected_analyzer_response)?
    );
    Ok(())
}

方法 2: 独自の extension を使う

独自の extension を定義する方法もあります。

Extension trait の validation method は ValidationResult を扱っています。 ValidationResult には complexitydepth フィールドを持つため、そこから確認できます。

https://github.com/bouzuya/rust-examples/blob/1aa40081b65db1aa969ea29797463983fc67d298/async-graphql6/src/main.rs#L110-L166

async fn test_my_extension() -> anyhow::Result<()> {
    struct MyExtensionFactory;

    impl async_graphql::extensions::ExtensionFactory for MyExtensionFactory {
        fn create(&self) -> std::sync::Arc<dyn async_graphql::extensions::Extension> {
            std::sync::Arc::new(MyExtension)
        }
    }

    struct MyExtension;

    #[async_trait::async_trait]
    impl async_graphql::extensions::Extension for MyExtension {
        async fn validation(
            &self,
            ctx: &async_graphql::extensions::ExtensionContext<'_>,
            next: async_graphql::extensions::NextValidation<'_>,
        ) -> Result<async_graphql::ValidationResult, Vec<async_graphql::ServerError>>
        {
            let validation_result: async_graphql::ValidationResult = next.run(ctx).await?;

            // 任意の処理 (ここでは標準出力に書き込んでいる)
            println!(
                "complexity = {}, depth = {}",
                validation_result.complexity, validation_result.depth
            );

            Ok(validation_result)
        }
    }

    async fn test_graphql_request(
        request: impl Into<async_graphql::Request>,
        expected_response: &str,
    ) -> anyhow::Result<()> {
        let schema = async_graphql::Schema::build(
            MyObject,
            async_graphql::EmptyMutation,
            async_graphql::EmptySubscription,
        )
        .extension(MyExtensionFactory)
        .finish();
        let request: async_graphql::Request = request.into();
        let response: async_graphql::Response = schema.execute(request).await;
        assert_eq!(
            serde_json::Value::from_str(&serde_json::to_string(&response)?)?,
            serde_json::Value::from_str(&expected_response)?
        );
        Ok(())
    }

    let request = r#"{ int }"#;
    let expected_response = r#"{"data":{"int":123}}"#;
    test_graphql_request(request, expected_response).await?;

    Ok(())
}

ExtensionFactory trait の実装から Extension trait の実装を返し、 Extension::validationNextValidation::run を呼び出せば ValidationResult が得られます。

SchemaBuilder::extensionExtensionFactory trait の実装を設定すれば OK です。

おわりに

async-graphql の query の complexity と depth の値を確認する方法について書きました。

GitHubで編集を提案
ドクターメイト

Discussion