-
Notifications
You must be signed in to change notification settings - Fork 11.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[GraphQL/Limits] Separate out directive checks
## Description Trying to do the directive checks at the same time as the query checks complicates both implementations. Split out the directive check into its own extension. Also fix the directive checks not looking at directives on variable definitions. ## Test plan ``` sui-graphql-e2e-tests$ cargo nextest run \ --features pg_integration \ -- limits/directives.move ```
- Loading branch information
Showing
7 changed files
with
149 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
crates/sui-graphql-rpc/src/extensions/directive_checker.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use std::sync::Arc; | ||
|
||
use async_graphql::{ | ||
extensions::{Extension, ExtensionContext, ExtensionFactory, NextParseQuery}, | ||
parser::types::{Directive, ExecutableDocument, Selection}, | ||
Positioned, ServerResult, | ||
}; | ||
use async_graphql_value::Variables; | ||
use async_trait::async_trait; | ||
|
||
use crate::error::{code, graphql_error_at_pos}; | ||
|
||
const ALLOWED_DIRECTIVES: [&str; 2] = ["include", "skip"]; | ||
|
||
/// Extension factory to add a check that all the directives used in the query are accepted and | ||
/// understood by the service. | ||
pub(crate) struct DirectiveChecker; | ||
|
||
struct DirectiveCheckerExt; | ||
|
||
impl ExtensionFactory for DirectiveChecker { | ||
fn create(&self) -> Arc<dyn Extension> { | ||
Arc::new(DirectiveCheckerExt) | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl Extension for DirectiveCheckerExt { | ||
async fn parse_query( | ||
&self, | ||
ctx: &ExtensionContext<'_>, | ||
query: &str, | ||
variables: &Variables, | ||
next: NextParseQuery<'_>, | ||
) -> ServerResult<ExecutableDocument> { | ||
let doc = next.run(ctx, query, variables).await?; | ||
|
||
let mut selection_sets = vec![]; | ||
for fragment in doc.fragments.values() { | ||
check_directives(&fragment.node.directives)?; | ||
selection_sets.push(&fragment.node.selection_set); | ||
} | ||
|
||
for (_name, op) in doc.operations.iter() { | ||
check_directives(&op.node.directives)?; | ||
|
||
for var in &op.node.variable_definitions { | ||
check_directives(&var.node.directives)?; | ||
} | ||
|
||
selection_sets.push(&op.node.selection_set); | ||
} | ||
|
||
while let Some(selection_set) = selection_sets.pop() { | ||
for selection in &selection_set.node.items { | ||
match &selection.node { | ||
Selection::Field(field) => { | ||
check_directives(&field.node.directives)?; | ||
selection_sets.push(&field.node.selection_set); | ||
} | ||
Selection::FragmentSpread(spread) => { | ||
check_directives(&spread.node.directives)?; | ||
} | ||
Selection::InlineFragment(fragment) => { | ||
check_directives(&fragment.node.directives)?; | ||
selection_sets.push(&fragment.node.selection_set); | ||
} | ||
} | ||
} | ||
} | ||
|
||
Ok(doc) | ||
} | ||
} | ||
|
||
fn check_directives(directives: &[Positioned<Directive>]) -> ServerResult<()> { | ||
for directive in directives { | ||
let name = &directive.node.name.node; | ||
if !ALLOWED_DIRECTIVES.contains(&name.as_str()) { | ||
let supported: Vec<_> = ALLOWED_DIRECTIVES | ||
.iter() | ||
.map(|s| format!("`@{s}`")) | ||
.collect(); | ||
|
||
return Err(graphql_error_at_pos( | ||
code::BAD_USER_INPUT, | ||
format!( | ||
"Directive `@{name}` is not supported. Supported directives are {}", | ||
supported.join(", "), | ||
), | ||
directive.pos, | ||
)); | ||
} | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
pub(crate) mod directive_checker; | ||
pub(crate) mod feature_gate; | ||
pub(crate) mod logger; | ||
pub mod query_limits_checker; | ||
pub(crate) mod query_limits_checker; | ||
pub(crate) mod timeout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters