Skip to content

Validation error using '@include' directive if schema comes from introspection #278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
SuperBo opened this issue Dec 9, 2021 · 10 comments
Closed
Labels
type: bug An issue or pull request relating to a bug

Comments

@SuperBo
Copy link

SuperBo commented Dec 9, 2021

When I try to use "@include" directive in my string query, I got this error:
graphql.error.graphql_error.GraphQLError: Unknown directive '@include'.

I think "@include" and "@Skip" are in standards of GraphQL https://graphql.org/learn/queries/#directives

Version in my environment

gql[requests]==3.0.0b1
graphql-core==3.1.6; python_version >= '3.6' and python_version < '4'

GraphQL Query

query fetchPool($with_swap: Boolean!) {
  pool(id: "0x83abecf7204d5afc1bea5df734f085f2535a9976") {
    id
    createdAtTimestamp
    swaps (first: 2) @include(if: $with_swap ) {
      id
      timestamp
    }
  }
}

Full stack trace:

Traceback (most recent call last):
File "test.py", line 27, in
result = client.execute(q, variable_values= {'with_swap': True})
File "/Users/ynguyen/.local/share/virtualenvs/activelp_v1_prod-cEysbnrO/lib/python3.8/site-packages/gql/client.py", line 186, in execute
return self.execute_sync(document, *args, **kwargs)
File "/Users/ynguyen/.local/share/virtualenvs/activelp_v1_prod-cEysbnrO/lib/python3.8/site-packages/gql/client.py", line 134, in execute_sync
return session.execute(document, *args, **kwargs)
File "/Users/ynguyen/.local/share/virtualenvs/activelp_v1_prod-cEysbnrO/lib/python3.8/site-packages/gql/client.py", line 389, in execute
result = self._execute(
File "/Users/ynguyen/.local/share/virtualenvs/activelp_v1_prod-cEysbnrO/lib/python3.8/site-packages/gql/client.py", line 328, in _execute
self.client.validate(document)
File "/Users/ynguyen/.local/share/virtualenvs/activelp_v1_prod-cEysbnrO/lib/python3.8/site-packages/gql/client.py", line 129, in validate
raise validation_errors[0]
graphql.error.graphql_error.GraphQLError: Unknown directive '@include'.

GraphQL request:6:22
5 | createdAtTimestamp
6 | swaps (first: 2) @include(if: $with_swap ) {
| ^
7 | id

@leszekhanusz
Copy link
Collaborator

Hi,

Which versions of gql and graphql-core are you using ?
Could you please post your query and the full stack trace ?

@SuperBo
Copy link
Author

SuperBo commented Dec 9, 2021

Hi @leszekhanusz , I've just updated my OP.

@leszekhanusz
Copy link
Collaborator

leszekhanusz commented Dec 9, 2021

How did you provide your schema? By using introspection or by providing your own schema.graphql file?
Does the schema include the @include directive? (I don't know if it is needed)

@SuperBo
Copy link
Author

SuperBo commented Dec 9, 2021

I set "fetch_schema_from_transport" in client to True. I found out that when I switch "fetch_schema_from_transport" to False, the query can work now.

This my full test code if you interest

import gql
from gql.transport.requests import RequestsHTTPTransport

query = """
query fetchPool($with_swap: Boolean!) {
  pool(id: "0x83abecf7204d5afc1bea5df734f085f2535a9976") {
    id
    createdAtTimestamp
    swaps (first: 2) @include(if: $with_swap ) {
      id
      timestamp
    }
  }
} 
"""

UNISWAP_V3_ENDPOINT = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'

transport = RequestsHTTPTransport(
    url=UNISWAP_V3_ENDPOINT, verify=True, retries=3,
)
client = gql.Client(transport=transport, fetch_schema_from_transport=True)

q = gql.gql(query)

result = client.execute(q, variable_values= {'with_swap': True})

print(result)

@leszekhanusz leszekhanusz changed the title Parse error when use '@include' directive Validation error when use '@include' directive Dec 9, 2021
@leszekhanusz
Copy link
Collaborator

I tried to valide your query using a provided schema.graphql file but I had the following error:

graphql.error.graphql_error.GraphQLError: Field 'pool' argument 'subgraphError' of type '_SubgraphErrorPolicy_!' is required, but it was not provided.

GraphQL request:3:3
2 | query fetchPool($with_swap: Boolean!) {
3 |   pool(id: "0x83abecf7204d5afc1bea5df734f085f2535a9976") {
  |   ^
4 |     id

This was solved by adding the required subgraphError field:

import gql                                                                                                              
from gql.transport.requests import RequestsHTTPTransport                                                                
                                                                                                                        
query = """                                                                                                             
query fetchPool($with_swap: Boolean!) {                                                                                 
  pool(id: "0x83abecf7204d5afc1bea5df734f085f2535a9976", subgraphError: allow) {                                        
    id                                                                                                                  
    createdAtTimestamp                                                                                                  
    swaps (first: 2) @include(if: $with_swap ) {                                                                        
      id                                                                                                                
      timestamp                                                                                                         
    }                                                                                                                   
  }                                                                                                                     
}                                                                                                                       
"""                                                                                                                     
                                                                                                                        
UNISWAP_V3_ENDPOINT = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'                                      
                                                                                                                        
with open("./schema.graphql") as f:                                                                                     
    schema_str = f.read()                                                                                               
                                                                                                                        
transport = RequestsHTTPTransport(                                                                                      
    url=UNISWAP_V3_ENDPOINT, verify=True, retries=3,                                                                    
)                                                                                                                       
client = gql.Client(transport=transport, schema=schema_str)                                                             
                                                                                                                        
q = gql.gql(query)                                                                                                      
                                                                                                                        
result = client.execute(q, variable_values= {'with_swap': True})                                                        
                                                                                                                        
print(result)

schema.graphql.zip

This is probably a bug in graphql-core. It is not normal that the include directive is not supported if the schema is fetched from the backend using introspection.

@leszekhanusz leszekhanusz added the type: bug An issue or pull request relating to a bug label Dec 9, 2021
@leszekhanusz leszekhanusz changed the title Validation error when use '@include' directive Validation error using '@include' directive if schema comes from introspection Dec 9, 2021
@leszekhanusz
Copy link
Collaborator

Thanks for your report!

@leszekhanusz
Copy link
Collaborator

@Cito could you please take a look at this bug?

@leszekhanusz
Copy link
Collaborator

leszekhanusz commented Dec 9, 2021

@leszekhanusz I don't see where the problem is. I can run your example without error.

@Cito I have received your message in a notification email but it does not appear here. GitHub having some issues perhaps?

Anyway to answer your question:
Yes, that's the point, it works if the schema is built from a file but not if the schema is built from introspection.
Ultimately, the problem lies in differences between the build_ast_schema and build_client_schema of graphql-core.

In build_ast_schema, there is this code to add the specified directives:

    # If specified directives were not explicitly declared, add them.                                                   
    if not any(directive.name == "skip" for directive in directives):                                                   
        directives.append(GraphQLSkipDirective)                                                                         
    if not any(directive.name == "include" for directive in directives):                                                
        directives.append(GraphQLIncludeDirective)                                                                      
    if not any(directive.name == "deprecated" for directive in directives):                                             
        directives.append(GraphQLDeprecatedDirective)                                                                   
    if not any(directive.name == "specifiedBy" for directive in directives):                                            
        directives.append(GraphQLSpecifiedByDirective)

There is nothing corresponding in the build_client_schema function.

@Cito
Copy link
Member

Cito commented Dec 9, 2021

Hi @leszekhanusz - yes, I understood the problem after reading again and therefore deleted my comment.

As you already noticed, the problem is the different behavior of build_client_schema and build_ast_schema which is inherited from GraphQL.js. The default directives like include are not added automatically with build_client_schema. As a workaround, you could do something like this to "normalize" the schema after creating it from introspection:

self.client.schema = build_ast_schema(parse(print_schema(self.client.schema)))

Or, you could somehow fumble the missing default directives into introspection['__schema']['directives'] before calling build_client_schema.
All of this is ugly, that's why I opened that ticket with GraphQL.js now. If it is fixed there, I will port it to GraphQL-core then.

@leszekhanusz
Copy link
Collaborator

Fixed in version 3.0.0rc0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug An issue or pull request relating to a bug
Projects
None yet
Development

No branches or pull requests

3 participants