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

feat: added object_type_name_prefix #1466

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Required library
Sphinx==1.5.3
jinja2<3.1.0
tcleonard marked this conversation as resolved.
Show resolved Hide resolved
sphinx-autobuild==0.7.1
# Docs template
https://graphene-python.org/sphinx_graphene_theme.zip
150 changes: 150 additions & 0 deletions docs/types/schema.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,153 @@ To disable this behavior, set the ``auto_camelcase`` to ``False`` upon schema in
query=MyRootQuery,
auto_camelcase=False,
)

.. _SchemaTypeNamePrefix:

Type name prefix
--------------------------

You can specify a prefix for all type names in the schema by setting the ``type_name_prefix`` argument upon schema instantiation:

.. code:: python

my_schema = Schema(
query=MyRootQuery,
mutation=MyRootMutation,
subscription=MyRootSubscription
type_name_prefix='MyPrefix',
)

This is useful in a micro-services architecture to prepend the service name to all types and avoid conflicts for example.

The prefix will be added to the name of:

* Query / Mutation / Subscription
* Scalar
* ObjectType
* InputType
* Enum
* Interface
* Union

While fields and arguments name will be left untouched.

More specifically, the following schema:

.. code::

type Query {
inner: MyType
}

type MyType {
field: String
myUnion: MyUnion
myBarType: MyBarType
myFooType: MyFooType
}

union MyUnion = MyBarType | MyFooType

type MyBarType {
field(input: MyInputObjectType): String
myInterface: MyInterface
}

input MyInputObjectType {
field: String
}

interface MyInterface {
field: String
}

type MyFooType {
field: String
myEnum: MyEnum
}

scalar MyScalar

enum MyEnum {
FOO
BAR
}

type Mutation {
createUser(name: String): CreateUser
}

type CreateUser {
name: String
}

type Subscription {
countToTen: Int
}

Will be transformed to:

.. code::

type Query {
myPrefixInner: MyPrefixMyType
}

type MyPrefixMyType {
field: String
myUnion: MyPrefixMyUnion
myBarType: MyPrefixMyBarType
myFooType: MyPrefixMyFooType
}

union MyPrefixMyUnion = MyPrefixMyBarType | MyPrefixMyFooType

type MyPrefixMyBarType {
field(input: MyPrefixMyInputObjectType): String
myInterface: MyPrefixMyInterface
}

input MyPrefixMyInputObjectType {
field: String
}

interface MyPrefixMyInterface {
field: String
}

type MyPrefixMyFooType {
field: String
myEnum: MyPrefixMyEnum
}

scalar MyPrefixMyScalar

enum MyPrefixMyEnum {
FOO
BAR
}

type Mutation {
myPrefixCreateUser(name: String): MyPrefixCreateUser
}

type MyPrefixCreateUser {
name: String
}

type Subscription {
myPrefixCountToTen: Int
}

You can override the prefix for a specific type by setting the ``type_name_prefix`` property on the ``Meta`` class:

.. code:: python

from graphene import ObjectType

class MyGraphQlType(ObjectType):
class Meta:
type_name_prefix = ''

This is useful when defining external types in a federated schema for example.
3 changes: 2 additions & 1 deletion graphene/types/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ def create_type(cls, class_name, **options):

@classmethod
def __init_subclass_with_meta__(
cls, name=None, description=None, _meta=None, **_kwargs
cls, name=None, description=None, type_name_prefix=None, _meta=None, **_kwargs
):
assert "_meta" not in cls.__dict__, "Can't assign meta directly"
if not _meta:
return
_meta.name = name or cls.__name__
_meta.description = description or trim_docstring(cls.__doc__)
_meta.type_name_prefix = type_name_prefix
_meta.freeze()
cls._meta = _meta
super(BaseType, cls).__init_subclass_with_meta__()
73 changes: 60 additions & 13 deletions graphene/types/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from .scalars import ID, Boolean, Float, Int, Scalar, String
from .structures import List, NonNull
from .union import Union
from .utils import get_field_as
from .utils import get_field_as, get_type_name

introspection_query = get_introspection_query()
IntrospectionSchema = introspection_types["__Schema"]
Expand Down Expand Up @@ -91,6 +91,7 @@ def __init__(
subscription=None,
types=None,
auto_camelcase=True,
type_name_prefix=None,
):
assert_valid_root_type(query)
assert_valid_root_type(mutation)
Expand All @@ -101,9 +102,18 @@ def __init__(
assert is_graphene_type(type_)

self.auto_camelcase = auto_camelcase
self.type_name_prefix = type_name_prefix

create_graphql_type = self.add_type

self.root_type_names = []
if query:
self.root_type_names.append(query._meta.name)
if mutation:
self.root_type_names.append(mutation._meta.name)
if subscription:
self.root_type_names.append(subscription._meta.name)

self.query = create_graphql_type(query) if query else None
self.mutation = create_graphql_type(mutation) if mutation else None
self.subscription = create_graphql_type(subscription) if subscription else None
Expand Down Expand Up @@ -131,9 +141,9 @@ def add_type(self, graphene_type):
elif issubclass(graphene_type, Interface):
graphql_type = self.create_interface(graphene_type)
elif issubclass(graphene_type, Scalar):
graphql_type = self.create_scalar(graphene_type)
graphql_type = self.create_scalar(graphene_type, self.type_name_prefix)
elif issubclass(graphene_type, Enum):
graphql_type = self.create_enum(graphene_type)
graphql_type = self.create_enum(graphene_type, self.type_name_prefix)
elif issubclass(graphene_type, Union):
graphql_type = self.construct_union(graphene_type)
else:
Expand All @@ -142,7 +152,10 @@ def add_type(self, graphene_type):
return graphql_type

@staticmethod
def create_scalar(graphene_type):
def create_scalar(
graphene_type,
type_name_prefix=None,
):
# We have a mapping to the original GraphQL types
# so there are no collisions.
_scalars = {
Expand All @@ -157,15 +170,15 @@ def create_scalar(graphene_type):

return GrapheneScalarType(
graphene_type=graphene_type,
name=graphene_type._meta.name,
name=get_type_name(graphene_type, type_name_prefix),
description=graphene_type._meta.description,
serialize=getattr(graphene_type, "serialize", None),
parse_value=getattr(graphene_type, "parse_value", None),
parse_literal=getattr(graphene_type, "parse_literal", None),
tcleonard marked this conversation as resolved.
Show resolved Hide resolved
)

@staticmethod
erikwrede marked this conversation as resolved.
Show resolved Hide resolved
def create_enum(graphene_type):
def create_enum(graphene_type, type_name_prefix=None):
values = {}
for name, value in graphene_type._meta.enum.__members__.items():
description = getattr(value, "description", None)
Expand Down Expand Up @@ -193,7 +206,7 @@ def create_enum(graphene_type):
return GrapheneEnumType(
graphene_type=graphene_type,
values=values,
name=graphene_type._meta.name,
name=get_type_name(graphene_type, type_name_prefix),
description=type_description,
)

Expand All @@ -215,9 +228,14 @@ def interfaces():
else:
is_type_of = graphene_type.is_type_of

if graphene_type._meta.name in self.root_type_names:
name = graphene_type._meta.name
else:
name = self.get_type_name(graphene_type)

return GrapheneObjectType(
graphene_type=graphene_type,
name=graphene_type._meta.name,
name=name,
description=graphene_type._meta.description,
fields=partial(self.create_fields_for_type, graphene_type),
is_type_of=is_type_of,
Expand All @@ -243,7 +261,7 @@ def interfaces():

return GrapheneInterfaceType(
graphene_type=graphene_type,
name=graphene_type._meta.name,
name=self.get_type_name(graphene_type),
description=graphene_type._meta.description,
fields=partial(self.create_fields_for_type, graphene_type),
interfaces=interfaces,
Expand All @@ -253,7 +271,7 @@ def interfaces():
def create_inputobjecttype(self, graphene_type):
return GrapheneInputObjectType(
graphene_type=graphene_type,
name=graphene_type._meta.name,
name=self.get_type_name(graphene_type),
description=graphene_type._meta.description,
out_type=graphene_type._meta.container,
fields=partial(
Expand Down Expand Up @@ -282,7 +300,7 @@ def types():

return GrapheneUnionType(
graphene_type=graphene_type,
name=graphene_type._meta.name,
name=self.get_type_name(graphene_type),
description=graphene_type._meta.description,
types=types,
resolve_type=resolve_type,
Expand Down Expand Up @@ -357,7 +375,10 @@ def create_fields_for_type(self, graphene_type, is_input_type=False):
deprecation_reason=field.deprecation_reason,
description=field.description,
)
field_name = field.name or self.get_name(name)
if field.name:
field_name = field.name
else:
field_name = self.get_field_name(graphene_type, name)
fields[field_name] = _field
return fields

Expand Down Expand Up @@ -391,6 +412,26 @@ def resolve_type(self, resolve_type_func, type_name, root, info, _type):
return_type = self[type_name]
return default_type_resolver(root, info, return_type)

def get_type_name(self, graphene_type):
return get_type_name(graphene_type, self.type_name_prefix)

def get_field_name(self, graphene_type, name):
if graphene_type._meta.name in self.root_type_names:
# We only add the prefix to the root types and types defined prefixes take precedence
# over schema defined prefix.
type_name_prefix = (
Comment on lines +415 to +422
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both methods should either be defined here or in utils.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @erikwrede sorry I haven't gotten back to you yet, I'm busy at work at the moment but I haven't forgotten about this PR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@superlevure great to hear from u! no worries, feel free to tag me once you get to it 🙂

graphene_type._meta.type_name_prefix
if graphene_type._meta.type_name_prefix is not None
else self.type_name_prefix
)
if type_name_prefix:
if self.auto_camelcase:
return to_camel_case(
type_name_prefix[0].lower() + type_name_prefix[1:] + "_" + name
)
return type_name_prefix + name
return self.get_name(name)


class Schema:
"""Schema Definition.
Expand Down Expand Up @@ -421,12 +462,18 @@ def __init__(
types=None,
directives=None,
auto_camelcase=True,
type_name_prefix=None,
):
self.query = query
self.mutation = mutation
self.subscription = subscription
type_map = TypeMap(
query, mutation, subscription, types, auto_camelcase=auto_camelcase
query,
mutation,
subscription,
types,
auto_camelcase=auto_camelcase,
type_name_prefix=type_name_prefix,
)
self.graphql_schema = GraphQLSchema(
type_map.query,
Expand Down
Loading