Skip to content
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

Fix support of typing.Any in GraphQL schema #317

Merged
merged 2 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions apischema/graphql/resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ def __init__(
pass_through_options,
)

@property
def _factory(self) -> Callable[[type], SerializationMethod]:
raise NotImplementedError

def enum(self, cls: Type[Enum]) -> SerializationMethod:
return IDENTITY_METHOD

Expand Down
13 changes: 5 additions & 8 deletions apischema/graphql/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,7 @@
to_camel_case,
)

JsonScalar = graphql.GraphQLScalarType("JSON")
if graphql.version_info >= (3, 1, 2):
JsonScalar.specified_by_url = (
"http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf"
)
JSON_SCALAR = graphql.GraphQLScalarType("JSON")
GRAPHQL_PRIMITIVE_TYPES = {
int: graphql.GraphQLInt,
float: graphql.GraphQLFloat,
Expand Down Expand Up @@ -229,7 +225,8 @@ def name_cache(
name: Optional[str], description: Optional[str]
) -> graphql.GraphQLNonNull:
if name is None:
return graphql.GraphQLNonNull(factory.factory(name, description)) # type: ignore
tp = factory.factory(name, description) # type: ignore
return graphql.GraphQLNonNull(tp) if tp is not JSON_SCALAR else tp
# Method is in cache key because scalar types will have the same method,
# and then be shared by both visitors, while input/output types will have
# their own cache entry.
Expand Down Expand Up @@ -306,7 +303,7 @@ def factory(
name: Optional[str], description: Optional[str]
) -> graphql.GraphQLScalarType:
if name is None:
return JsonScalar
return JSON_SCALAR
else:
return graphql.GraphQLScalarType(name, description=description)

Expand Down Expand Up @@ -366,7 +363,7 @@ def factory(
if name is not None:
return graphql.GraphQLScalarType(name, description=description)
else:
return JsonScalar
return JSON_SCALAR

return TypeFactory(factory)

Expand Down
11 changes: 10 additions & 1 deletion docs/graphql/data_model_and_resolvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,16 @@ Following the example of [type/field/method base schema](../json_schema.md#base-
{!base_schema_parameter.py!}
```

## Scalars

`NewType` or non-object types annotated with `type_name` will be translated in the GraphQL schema by a `scalar`. By the way, `Any` will automatically be translated to a `JSON` scalar, as it is deserialized from and serialized to JSON.

```python
{!scalar.py!}
```

## ID type
GraphQL `ID` has no precise specification and is defined according API needs; it can be a UUID or and ObjectId, etc.
GraphQL `ID` has no precise specification and is defined according API needs; it can be a UUID or/and ObjectId, etc.

`apischema.graphql_schema` has a parameter `id_types` which can be used to define which types will be marked as `ID` in the generated schema. Parameter value can be either a collection of types (each type will then be mapped to `ID` scalar), or a predicate returning if the given type must be marked as `ID`.

Expand Down Expand Up @@ -134,6 +142,7 @@ GraphQL `ID` has no precise specification and is defined according API needs; it

If you use base64 encodeing and an ID type which is converted by *apischema* to a base64 str, you will get a double encoded base64 string


## Tagged unions

!!! important
Expand Down
35 changes: 35 additions & 0 deletions examples/scalar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from dataclasses import dataclass
from typing import Any
from uuid import UUID

from graphql.utilities import print_schema

from apischema.graphql import graphql_schema


@dataclass
class Foo:
id: UUID
content: Any


def foo() -> Foo | None:
...


schema = graphql_schema(query=[foo])
schema_str = """\
type Query {
foo: Foo
}

type Foo {
id: UUID!
content: JSON
}

scalar UUID

scalar JSON
"""
assert print_schema(schema) == schema_str