Skip to content

Commit

Permalink
Add skip and include directive in introspection schema
Browse files Browse the repository at this point in the history
  • Loading branch information
leszekhanusz committed Dec 9, 2021
1 parent 2be6aaa commit d9b85de
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 2 deletions.
2 changes: 1 addition & 1 deletion gql/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
ExecutionResult,
GraphQLSchema,
build_ast_schema,
build_client_schema,
get_introspection_query,
parse,
validate,
Expand All @@ -17,6 +16,7 @@
from .transport.exceptions import TransportQueryError
from .transport.local_schema import LocalSchemaTransport
from .transport.transport import Transport
from .utilities import build_client_schema
from .utilities import parse_result as parse_result_fn
from .utilities import serialize_variable_values

Expand Down
2 changes: 2 additions & 0 deletions gql/utilities/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from .build_client_schema import build_client_schema
from .get_introspection_query_ast import get_introspection_query_ast
from .parse_result import parse_result
from .serialize_variable_values import serialize_value, serialize_variable_values
from .update_schema_enum import update_schema_enum
from .update_schema_scalars import update_schema_scalar, update_schema_scalars

__all__ = [
"build_client_schema",
"parse_result",
"get_introspection_query_ast",
"serialize_variable_values",
Expand Down
73 changes: 73 additions & 0 deletions gql/utilities/build_client_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from typing import Dict

from graphql import GraphQLSchema
from graphql import build_client_schema as build_client_schema_orig

__all__ = ["build_client_schema"]


INCLUDE_DIRECTIVE_JSON = {
"name": "include",
"description": (
"Directs the executor to include this field or fragment "
"only when the `if` argument is true."
),
"locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
"args": [
{
"name": "if",
"description": "Included when true.",
"type": {
"kind": "NON_NULL",
"name": "None",
"ofType": {"kind": "SCALAR", "name": "Boolean", "ofType": "None"},
},
"defaultValue": "None",
}
],
}

SKIP_DIRECTIVE_JSON = {
"name": "skip",
"description": (
"Directs the executor to skip this field or fragment "
"when the `if` argument is true."
),
"locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
"args": [
{
"name": "if",
"description": "Skipped when true.",
"type": {
"kind": "NON_NULL",
"name": "None",
"ofType": {"kind": "SCALAR", "name": "Boolean", "ofType": "None"},
},
"defaultValue": "None",
}
],
}


def build_client_schema(introspection: Dict) -> GraphQLSchema:
"""This is an alternative to the graphql-core function
:code:`build_client_schema` but with default include and skip directives
added to the schema to fix
`issue #278 <https://github.com/graphql-python/gql/issues/278>`_
.. warning::
This function will be removed once the issue
`graphql-js#3419 <https://github.com/graphql/graphql-js/issues/3419>`_
has been fixed and ported to graphql-core so don't use it
outside gql.
"""

directives = introspection["__schema"]["directives"]

if not any(directive["name"] == "skip" for directive in directives):
directives.append(SKIP_DIRECTIVE_JSON)

if not any(directive["name"] == "include" for directive in directives):
directives.append(INCLUDE_DIRECTIVE_JSON)

return build_client_schema_orig(introspection, assume_valid=False)
48 changes: 47 additions & 1 deletion tests/starwars/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,24 @@ def introspection_schema():
return Client(introspection=StarWarsIntrospection)


@pytest.fixture(params=["local_schema", "typedef_schema", "introspection_schema"])
@pytest.fixture
def introspection_schema_no_directives():
introspection = StarWarsIntrospection

# Simulate an empty dictionary for directives
introspection["__schema"]["directives"] = []

return Client(introspection=introspection)


@pytest.fixture(
params=[
"local_schema",
"typedef_schema",
"introspection_schema",
"introspection_schema_no_directives",
]
)
def client(request):
return request.getfixturevalue(request.param)

Expand Down Expand Up @@ -187,3 +204,32 @@ def test_allows_object_fields_in_inline_fragments(client):
}
"""
assert not validation_errors(client, query)


def test_include_directive(client):
query = """
query fetchHero($with_friends: Boolean!) {
hero {
name
friends @include(if: $with_friends) {
name
}
}
}
"""
assert not validation_errors(client, query)


def test_skip_directive(client):
query = """
query fetchHero($without_friends: Boolean!) {
hero {
name
friends @skip(if: $without_friends) {
name
}
}
}
"""
print(StarWarsIntrospection)
assert not validation_errors(client, query)

0 comments on commit d9b85de

Please sign in to comment.