mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-29 04:53:55 +03:00
add depth limit validator tests
This commit is contained in:
parent
a784ef15e5
commit
d7b474751d
|
@ -20,16 +20,26 @@ Example
|
||||||
Here is how you would implement depth-limiting on your schema.
|
Here is how you would implement depth-limiting on your schema.
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
from graphql import validate
|
||||||
|
from graphene import ObjectType, Schema, String
|
||||||
from graphene.validation import depth_limit_validator
|
from graphene.validation import depth_limit_validator
|
||||||
|
|
||||||
# The following schema doesn't execute queries
|
|
||||||
# which have a depth more than 20.
|
|
||||||
|
|
||||||
result = schema.execute(
|
class MyQuery(ObjectType):
|
||||||
'THE QUERY',
|
name = String(required=True)
|
||||||
validation_rules=[
|
|
||||||
|
|
||||||
|
schema = Schema(query=MyQuery)
|
||||||
|
|
||||||
|
# Queries which have a depth more than 20
|
||||||
|
# will not be executed.
|
||||||
|
|
||||||
|
validation_errors = validate(
|
||||||
|
schema=schema,
|
||||||
|
document='THE QUERY',
|
||||||
|
rules=(
|
||||||
depth_limit_validator(
|
depth_limit_validator(
|
||||||
max_depth=20
|
max_depth=20
|
||||||
)
|
),
|
||||||
]
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,4 +3,4 @@ def is_introspection_key(key):
|
||||||
# > All types and directives defined within a schema must not have a name which
|
# > All types and directives defined within a schema must not have a name which
|
||||||
# > begins with "__" (two underscores), as this is used exclusively
|
# > begins with "__" (two underscores), as this is used exclusively
|
||||||
# > by GraphQL’s introspection system.
|
# > by GraphQL’s introspection system.
|
||||||
return str(node.name.value).startswith("__")
|
return str(key).startswith("__")
|
||||||
|
|
279
graphene/validation/tests/test_depth_limit_validator.py
Normal file
279
graphene/validation/tests/test_depth_limit_validator.py
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
import re
|
||||||
|
|
||||||
|
from pytest import raises
|
||||||
|
from graphql import parse, get_introspection_query, validate
|
||||||
|
|
||||||
|
from ...types import Schema, ObjectType, Interface
|
||||||
|
from ...types import String, Int, List, Field
|
||||||
|
from ..depth_limit import depth_limit_validator
|
||||||
|
|
||||||
|
|
||||||
|
class PetType(Interface):
|
||||||
|
name = String(required=True)
|
||||||
|
|
||||||
|
class meta:
|
||||||
|
name = "Pet"
|
||||||
|
|
||||||
|
|
||||||
|
class CatType(ObjectType):
|
||||||
|
class meta:
|
||||||
|
name = "Cat"
|
||||||
|
interfaces = (PetType,)
|
||||||
|
|
||||||
|
|
||||||
|
class DogType(ObjectType):
|
||||||
|
class meta:
|
||||||
|
name = "Dog"
|
||||||
|
interfaces = (PetType,)
|
||||||
|
|
||||||
|
|
||||||
|
class AddressType(ObjectType):
|
||||||
|
street = String(required=True)
|
||||||
|
number = Int(required=True)
|
||||||
|
city = String(required=True)
|
||||||
|
country = String(required=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = "Address"
|
||||||
|
|
||||||
|
|
||||||
|
class HumanType(ObjectType):
|
||||||
|
name = String(required=True)
|
||||||
|
email = String(required=True)
|
||||||
|
address = Field(AddressType, required=True)
|
||||||
|
pets = List(PetType, required=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = "Human"
|
||||||
|
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
user = Field(
|
||||||
|
HumanType,
|
||||||
|
required=True,
|
||||||
|
name=String()
|
||||||
|
)
|
||||||
|
version = String(
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
user1 = Field(
|
||||||
|
HumanType,
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
user2 = Field(
|
||||||
|
HumanType,
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
user3 = Field(
|
||||||
|
HumanType,
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_user(root, info, name=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
|
||||||
|
def run_query(query: str, max_depth: int, ignore=None):
|
||||||
|
document = parse(query)
|
||||||
|
|
||||||
|
result = None
|
||||||
|
|
||||||
|
def callback(query_depths):
|
||||||
|
nonlocal result
|
||||||
|
result = query_depths
|
||||||
|
|
||||||
|
errors = validate(
|
||||||
|
schema.graphql_schema,
|
||||||
|
document,
|
||||||
|
rules=(
|
||||||
|
depth_limit_validator(
|
||||||
|
max_depth=max_depth,
|
||||||
|
ignore=ignore,
|
||||||
|
callback=callback
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return errors, result
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_count_depth_without_fragment():
|
||||||
|
query = """
|
||||||
|
query read0 {
|
||||||
|
version
|
||||||
|
}
|
||||||
|
query read1 {
|
||||||
|
version
|
||||||
|
user {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query read2 {
|
||||||
|
matt: user(name: "matt") {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
andy: user(name: "andy") {
|
||||||
|
email
|
||||||
|
address {
|
||||||
|
city
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query read3 {
|
||||||
|
matt: user(name: "matt") {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
andy: user(name: "andy") {
|
||||||
|
email
|
||||||
|
address {
|
||||||
|
city
|
||||||
|
}
|
||||||
|
pets {
|
||||||
|
name
|
||||||
|
owner {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
expected = {"read0": 0, "read1": 1, "read2": 2, "read3": 3}
|
||||||
|
|
||||||
|
errors, result = run_query(query, 10)
|
||||||
|
assert not errors
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_count_with_fragments():
|
||||||
|
query = """
|
||||||
|
query read0 {
|
||||||
|
... on Query {
|
||||||
|
version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query read1 {
|
||||||
|
version
|
||||||
|
user {
|
||||||
|
... on Human {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fragment humanInfo on Human {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
fragment petInfo on Pet {
|
||||||
|
name
|
||||||
|
owner {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query read2 {
|
||||||
|
matt: user(name: "matt") {
|
||||||
|
...humanInfo
|
||||||
|
}
|
||||||
|
andy: user(name: "andy") {
|
||||||
|
...humanInfo
|
||||||
|
address {
|
||||||
|
city
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query read3 {
|
||||||
|
matt: user(name: "matt") {
|
||||||
|
...humanInfo
|
||||||
|
}
|
||||||
|
andy: user(name: "andy") {
|
||||||
|
... on Human {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
address {
|
||||||
|
city
|
||||||
|
}
|
||||||
|
pets {
|
||||||
|
...petInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
expected = {"read0": 0, "read1": 1, "read2": 2, "read3": 3}
|
||||||
|
|
||||||
|
errors, result = run_query(query, 10)
|
||||||
|
assert not errors
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_ignore_the_introspection_query():
|
||||||
|
errors, result = run_query(get_introspection_query(), 10)
|
||||||
|
assert not errors
|
||||||
|
assert result == {"IntrospectionQuery": 0}
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_catch_very_deep_query():
|
||||||
|
query = """{
|
||||||
|
user {
|
||||||
|
pets {
|
||||||
|
owner {
|
||||||
|
pets {
|
||||||
|
owner {
|
||||||
|
pets {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
errors, result = run_query(query, 4)
|
||||||
|
|
||||||
|
assert len(errors) == 1
|
||||||
|
assert errors[0].message == "'anonymous' exceeds maximum operation depth of 4"
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_ignore_field():
|
||||||
|
query = """
|
||||||
|
query read1 {
|
||||||
|
user { address { city } }
|
||||||
|
}
|
||||||
|
query read2 {
|
||||||
|
user1 { address { city } }
|
||||||
|
user2 { address { city } }
|
||||||
|
user3 { address { city } }
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
errors, result = run_query(
|
||||||
|
query,
|
||||||
|
10,
|
||||||
|
ignore=[
|
||||||
|
"user1",
|
||||||
|
re.compile("user2"),
|
||||||
|
lambda field_name: field_name == "user3",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
expected = {"read1": 2, "read2": 0}
|
||||||
|
assert not errors
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_raise_invalid_ignore():
|
||||||
|
query = """
|
||||||
|
query read1 {
|
||||||
|
user { address { city } }
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
with raises(ValueError, match="Invalid ignore option:"):
|
||||||
|
run_query(
|
||||||
|
query,
|
||||||
|
10,
|
||||||
|
ignore=[True],
|
||||||
|
)
|
|
@ -18,10 +18,6 @@ def run_query(query: str):
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
|
|
||||||
def callback(query_depths):
|
|
||||||
nonlocal result
|
|
||||||
result = query_depths
|
|
||||||
|
|
||||||
errors = validate(
|
errors = validate(
|
||||||
schema.graphql_schema,
|
schema.graphql_schema,
|
||||||
document,
|
document,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user