mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-25 19:13:57 +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.
|
||||
|
||||
.. code:: python
|
||||
from graphql import validate
|
||||
from graphene import ObjectType, Schema, String
|
||||
from graphene.validation import depth_limit_validator
|
||||
|
||||
# The following schema doesn't execute queries
|
||||
# which have a depth more than 20.
|
||||
|
||||
result = schema.execute(
|
||||
'THE QUERY',
|
||||
validation_rules=[
|
||||
class MyQuery(ObjectType):
|
||||
name = String(required=True)
|
||||
|
||||
|
||||
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(
|
||||
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
|
||||
# > begins with "__" (two underscores), as this is used exclusively
|
||||
# > 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
|
||||
|
||||
def callback(query_depths):
|
||||
nonlocal result
|
||||
result = query_depths
|
||||
|
||||
errors = validate(
|
||||
schema.graphql_schema,
|
||||
document,
|
||||
|
|
Loading…
Reference in New Issue
Block a user