mirumee/ariadne

`make_executable_schema`'s omits default enum values in args and inputs

Closed this issue · 14 comments

GraphQL enums are represented in Python via the GraphQLEnumType. If those types are created from the SDL, they define no Python representation for GraphQL enum items. Developers can use Ariadne-provided EnumType to specify how enum items should be represented in Python themselves, but we also include "fallback" step that fills enum values with their str representations.

However neither of those approaches takes care of enum items defined as default values for field arguments. Eg:

type Query {
    users(role: UserRole = ADMIN): [User]
}

input FooBarInput {
    foo: FooEnum! = FOO
}

We will need to add additional step to make_executable_schema that gathers all enums from schema together with their Python representations, and then introspects schema's fields values for places where field or input has default value type of enum. If this is the case, we should set correct default_value on GraphQLArgument in the schema.

Is there a workaround for this ?

Any update on this? The issue was created quite a while ago and feels like a pretty common use case. I've only been playing with ariadne for a couple days and hit the bug immediately.

PR's welcome :)

In the meantime workaround for this issue is to define default value in field's resolver, eg:

def resolve(_, info, *, role=Roles.ADMIN)

It's unclear to me why this is not already done by graphql.parse()
This is the function that interprets a raw schema string and converts it into python objects like GraphQLEnumType and GraphQLArgument, correct?
Why isn't the population of GraphQLArgument.default_value happening as part of that step?

graphql.parse() doesn't known about custom enum bindings defined in ariadne.EnumType.

The use of custom ariadne.EnumType bindings is optional, I thought. According to https://ariadnegraphql.org/docs/enums#mapping-to-internal-values - the default behaviour is for enums to be represented as strings. So if someone is not making use of custom enum bindings, can graphql.parse() take care of it?

That may be the case, but please remember that this issue is mainly about EnumType that Ariadne provides.

This is not the case as long as EnumType is not used. For this schema:

enum Role {
  ADMIN
  USER
}

type Query {
  hello(r: Role = USER): String
}

And this query:

{
  default: hello
  custom: hello(r: ADMIN)
}

Resolver that returns repr(r) gives this result:

{
  "default": "'USER'",
  "custom": "'ADMIN'"
}

However as soon as EnumType is used, python types stop working out:

{
  "default": "'USER'",
  "custom": "<Role.ADMIN: 'ADMIN'>"
}

Turns out there's also a gotcha with Schema validation in GraphQL core which skips default values validation against enum definition:

graphql-python/graphql-core#122

Without this its going to be super troublesome to offer useful error messages. I'll wait now to see what response to above issue will be given. If there's no, we may have to implement schema validator on our own for this case.

I don't know if this is the right place to discuss this issue (it seems related to enums and default values):

There seems to be a regression in ariadne 0.13.0. Or at least an unexpected change in behaviour when I upgraded from 0.12.0.

If you run the following:

import ariadne
import enum
import pprint

type_defs = """
    enum Role {
        ADMIN
        USER
    }

    type Query {
        hello(r: Role = USER): String
    }
"""

class Role(enum.Enum):
    ADMIN = "admin"
    USER = "user"

RoleGraphQLType = ariadne.EnumType("Role", Role)

QueryGraphQLType = ariadne.QueryType()

schema = ariadne.make_executable_schema(
    type_defs,
    QueryGraphQLType,
    RoleGraphQLType,
)

query = "{__schema{types{name,fields{name,args{name,defaultValue}}}}}"

result = ariadne.graphql_sync(schema, {"query": query}, debug=True)
pprint.pprint(result)

This works fine under the environment:

ariadne 0.12.0
graphql-core 3.0.5

But when run under the environment:

ariadne 0.13.0
graphql-core 3.1.3

It produces the error:

Traceback (most recent call last):
  File "venv/lib/python3.8/site-packages/graphql/execution/execute.py", line 678, in complete_value_catching_error
    completed = self.complete_value(
  File "venv/lib/python3.8/site-packages/graphql/execution/execute.py", line 745, in complete_value
    raise result
  File "venv/lib/python3.8/site-packages/graphql/execution/execute.py", line 637, in resolve_field_value_or_error
    result = resolve_fn(source, info, **args)
  File "venv/lib/python3.8/site-packages/graphql/type/introspection.py", line 399, in default_value
    value_ast = ast_from_value(item[1].default_value, item[1].type)
  File "venv/lib/python3.8/site-packages/graphql/utilities/ast_from_value.py", line 108, in ast_from_value
    serialized = type_.serialize(value)  # type: ignore
  File "venv/lib/python3.8/site-packages/graphql/type/definition.py", line 1146, in serialize
    raise GraphQLError(
graphql.error.graphql_error.GraphQLError: Enum 'Role' cannot represent value: 'USER'

Perhaps this is obvious but, to add on to @dkbarn's previous comment, as it stands the default introspection query used by tools like GraphiQL and GraphQL Playground do not work. This results in the documentation tab and the auto-fill being broken in those tools.

@dkbarn that looks like a bug all-right, but it should be separate issue on our tracker. Can you post it as separate bug? Thanks!

Sure thing, separate issue created here: #547

Has there been any movement on the schema validation solution/workaround that would unblock this issue? My team is working on a project that is currently using workarounds for this and we'd like to remove them.