DSL does not provide expected argument validation
brhubbar opened this issue · 1 comments
Describe the bug
- When creating a query with
gql.gql()
, the query string is checked for invalid arguments, and raises an exception if any are found. (expected behavior) - When creating a query with
gql.dsl.dsl_gql()
, the arguments are not checked, causing unexpected return values. (unexpected behavior)
I've been able to recreate this using the countries api used in the docs.
To Reproduce
Jump to step 5 to see the actual improper behavior.
-
Set up the transport/client.
import json import gql from gql.transport.requests import RequestsHTTPTransport as Transport from gql import dsl url = "https://countries.trevorblades.com/" transport = Transport(url=url) client = gql.Client(transport=transport, fetch_schema_from_transport=True) # Fetch the schema (lemme know if there's a recommended approach for this). client.connect_sync() client.close_sync() ds = dsl.DSLSchema(client.schema)
-
Run a good query using strings.
good_query_str = gql.gql( """ query { continents (filter:{code:{eq:"AN"}}) { code name } } """ ) result = client.execute(good_query_str) print(json.dumps(result, indent=2))
Result:
{ "continents": [ { "code": "AN", "name": "Antarctica" } ] }
-
Run a bad query using strings. The only change here is using
'AN'
directly as an argument tocode
, instead of providing theeq
directive.bad_query_str = gql.gql( """ query { continents (filter:{code:"AN"}) { code name } } """ ) result = client.execute(bad_query_str) print(json.dumps(result, indent=2))
Result:
GraphQLError: Expected value of type 'StringQueryOperatorInput', found "AN". GraphQL request:3:34 2 | query { 3 | continents (filter:{code:"AN"}) { | ^ 4 | code
-
Run a good query using DSL.
good_query_dsl = dsl.dsl_gql( dsl.DSLQuery( ds.Query.continents( filter={ 'code': {'eq': 'AN'} } ).select( ds.Continent.code, ds.Continent.name, ) ) ) result = client.execute(good_query_dsl) print(json.dumps(result, indent=2))
Result:
{ "continents": [ { "code": "AN", "name": "Antarctica" } ] }
-
Run a bad query using DSL. Same deal, just remove the 'eq' level of filter specification. Note that the result is an unfiltered response.
bad_query_dsl = dsl.dsl_gql( dsl.DSLQuery( ds.Query.continents( filter={ 'code': 'AN' } ).select( ds.Continent.code, ds.Continent.name, ) ) ) result = client.execute(bad_query_dsl) print(json.dumps(result, indent=2))
Result:
{ "continents": [ { "code": "AF", "name": "Africa" }, { "code": "AN", "name": "Antarctica" }, { "code": "AS", "name": "Asia" }, { "code": "EU", "name": "Europe" }, { "code": "NA", "name": "North America" }, { "code": "OC", "name": "Oceania" }, { "code": "SA", "name": "South America" } ] }
Expected behavior
Step 5 should raise an equivalent exception to step 3.
System info (please complete the following information):
- OS: Wins 10
- Python version: 3.9.12
- gql version: 3.4.0
- graphql-core version: 3.2.1
Poking at this a bit more (after finding gql.dsl.print_ast
), it looks like DSL silently cleanses your query of anything it doesn't recognize, so the example above:
from gql.dsl import print_ast
bad_query_dsl = dsl.dsl_gql(
dsl.DSLQuery(
ds.Query.continents(
filter={
'code': 'AN'
}
).select(
ds.Continent.code,
ds.Continent.name,
)
)
)
print(print_ast(bad_query_dsl))
Results in this query:
{
continents(filter: {}) {
code
name
}
}
This also happens in some (but not all) cases where an incorrect type is provided as a value. I've found that passing a list
where a string
is expected returns a very long traceback, ending with this:
in serialize_string(output_value)
174 # do not serialize builtin types as strings, but allow serialization of custom 175 # types via their `__str__` method
176 if type(output_value).__module__ == "builtins":
--> 177 raise GraphQLError("String cannot represent value: " + inspect(output_value))
178 return str(output_value)
GraphQLError: String cannot represent value: ['AN']
However, passing a string
or a dict
where a list of dict
is expected results in an empty list
in the query. Differently again, passing a string
where a list of string
is expected is accepted. I don't know if this is a gql
quirk or a GraphQL quirk.