diff --git a/docs/types/schema.rst b/docs/types/schema.rst index f491dd12..407dbe9b 100644 --- a/docs/types/schema.rst +++ b/docs/types/schema.rst @@ -114,11 +114,12 @@ This is useful in a micro-services architecture to prepend the service name to a The prefix will be added to the name of: * Query / Mutation / Subscription +* Scalar * ObjectType * InputType +* Enum * Interface * Union -* Enum While fields and arguments name will be left untouched. @@ -157,6 +158,8 @@ More specifically, the following schema: myEnum: MyEnum } + scalar MyScalar + enum MyEnum { FOO BAR @@ -209,6 +212,8 @@ Will be transformed to: myEnum: MyPrefixMyEnum } + scalar MyPrefixMyScalar + enum MyPrefixMyEnum { FOO BAR @@ -225,3 +230,15 @@ Will be transformed to: 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 = 'MyOtherPrefix' + +This is useful when defining external types in a federated schema for example. diff --git a/graphene/types/base.py b/graphene/types/base.py index 84cb377a..451851e0 100644 --- a/graphene/types/base.py +++ b/graphene/types/base.py @@ -36,13 +36,14 @@ class BaseType(SubclassWithMeta): @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__() diff --git a/graphene/types/schema.py b/graphene/types/schema.py index 340eedc3..e1ed9ceb 100644 --- a/graphene/types/schema.py +++ b/graphene/types/schema.py @@ -166,7 +166,7 @@ class TypeMap(dict): return GrapheneScalarType( graphene_type=graphene_type, - name=self.add_prefix_to_type_name(graphene_type._meta.name), + name=self.get_type_name(graphene_type), description=graphene_type._meta.description, serialize=getattr(graphene_type, "serialize", None), parse_value=getattr(graphene_type, "parse_value", None), @@ -201,7 +201,7 @@ class TypeMap(dict): return GrapheneEnumType( graphene_type=graphene_type, values=values, - name=self.add_prefix_to_type_name(graphene_type._meta.name), + name=self.get_type_name(graphene_type), description=type_description, ) @@ -226,7 +226,7 @@ class TypeMap(dict): if graphene_type._meta.name in self.root_type_names: name = graphene_type._meta.name else: - name = self.add_prefix_to_type_name(graphene_type._meta.name) + name = self.get_type_name(graphene_type) return GrapheneObjectType( graphene_type=graphene_type, @@ -256,7 +256,7 @@ class TypeMap(dict): return GrapheneInterfaceType( graphene_type=graphene_type, - name=self.add_prefix_to_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, @@ -266,7 +266,7 @@ class TypeMap(dict): def create_inputobjecttype(self, graphene_type): return GrapheneInputObjectType( graphene_type=graphene_type, - name=self.add_prefix_to_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( @@ -295,7 +295,7 @@ class TypeMap(dict): return GrapheneUnionType( graphene_type=graphene_type, - name=self.add_prefix_to_type_name(graphene_type._meta.name), + name=self.get_type_name(graphene_type), description=graphene_type._meta.description, types=types, resolve_type=resolve_type, @@ -373,10 +373,7 @@ class TypeMap(dict): if field.name: field_name = field.name else: - if graphene_type._meta.name in self.root_type_names: - field_name = self.add_prefix_to_field_name(name) - else: - field_name = self.get_name(name) + field_name = self.get_field_name(graphene_type, name) fields[field_name] = _field return fields @@ -410,21 +407,35 @@ class TypeMap(dict): return_type = self[type_name] return default_type_resolver(root, info, return_type) - def add_prefix_to_type_name(self, name): - if self.type_name_prefix: - return self.type_name_prefix[0].upper() + self.type_name_prefix[1:] + name - return name + def get_type_name(self, graphene_type): + type_name_prefix = ( + 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: + return ( + type_name_prefix[0].upper() + + type_name_prefix[1:] + + graphene_type._meta.name + ) + return graphene_type._meta.name - def add_prefix_to_field_name(self, name): - if self.type_name_prefix: - if self.auto_camelcase: - return self.get_name( - self.type_name_prefix[0].lower() - + self.type_name_prefix[1:] - + "_" - + name - ) - return self.get_name(self.type_name_prefix + name) + 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 = ( + 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) diff --git a/graphene/types/tests/test_schema.py b/graphene/types/tests/test_schema.py index 3ac0600b..22941970 100644 --- a/graphene/types/tests/test_schema.py +++ b/graphene/types/tests/test_schema.py @@ -167,137 +167,3 @@ def test_schema_requires_query_type(): assert len(result.errors) == 1 error = result.errors[0] assert error.message == "Query root type must be provided." - - -def test_schema_type_name_prefix_camelcase(): - schema = Schema( - Query, - Mutation, - Subscription, - auto_camelcase=True, - type_name_prefix="MyPrefix", - ) - assert ( - str(schema).strip() - == dedent( - """ - 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 - myScalar: MyPrefixMyScalar - myEnum: MyPrefixMyEnum - } - - scalar MyPrefixMyScalar - - enum MyPrefixMyEnum { - FOO - BAR - } - - type Mutation { - myPrefixCreateUser(name: String): MyPrefixCreateUser - } - - type MyPrefixCreateUser { - name: String - } - - type Subscription { - myPrefixCountToTen: Int - } - """ - ).strip() - ) - - -def test_schema_type_name_prefix_camelcase_disabled(): - schema = Schema( - Query, - Mutation, - Subscription, - auto_camelcase=False, - type_name_prefix="MyPrefix", - ) - assert ( - str(schema).strip() - == dedent( - """ - type Query { - MyPrefixinner: MyPrefixMyType - } - - type MyPrefixMyType { - field: String - my_union: MyPrefixMyUnion - my_bar_type: MyPrefixMyBarType - my_foo_type: MyPrefixMyFooType - } - - union MyPrefixMyUnion = MyPrefixMyBarType | MyPrefixMyFooType - - type MyPrefixMyBarType { - field(input: MyPrefixMyInputObjectType): String - my_interface: MyPrefixMyInterface - } - - input MyPrefixMyInputObjectType { - field: String - } - - interface MyPrefixMyInterface { - field: String - } - - type MyPrefixMyFooType { - field: String - my_scalar: MyPrefixMyScalar - my_enum: MyPrefixMyEnum - } - - scalar MyPrefixMyScalar - - enum MyPrefixMyEnum { - FOO - BAR - } - - type Mutation { - MyPrefixcreate_user(name: String): MyPrefixCreateUser - } - - type MyPrefixCreateUser { - name: String - } - - type Subscription { - MyPrefixcount_to_ten: Int - } - """ - ).strip() - ) diff --git a/graphene/types/tests/test_type_name_prefix.py b/graphene/types/tests/test_type_name_prefix.py new file mode 100644 index 00000000..add188ae --- /dev/null +++ b/graphene/types/tests/test_type_name_prefix.py @@ -0,0 +1,353 @@ +from textwrap import dedent + + +from ..field import Field +from ..enum import Enum +from ..inputobjecttype import InputObjectType +from ..interface import Interface +from ..mutation import Mutation as Mutation_ +from ..objecttype import ObjectType +from ..scalars import Int, String, Scalar +from ..schema import Schema +from ..union import Union + + +def test_schema_type_name_prefix_override(): + class MyInputObjectType(InputObjectType): + class Meta: + type_name_prefix = "OverridePrefix" + + field = String() + + class MyScalar(Scalar): + class Meta: + type_name_prefix = "" + + class MyEnum(Enum): + class Meta: + type_name_prefix = "" + + FOO = "foo" + BAR = "bar" + + class MyInterface(Interface): + class Meta: + type_name_prefix = "" + + field = String() + + class MyBarType(ObjectType): + class Meta: + type_name_prefix = "" + + field = String(input=MyInputObjectType()) + my_interface = Field(MyInterface) + my_scalar = MyScalar() + my_enum = MyEnum() + + class MyFooType(ObjectType): + class Meta: + type_name_prefix = "" + + field = String() + + class MyUnion(Union): + class Meta: + type_name_prefix = "" + types = (MyBarType, MyFooType) + + class MyType(ObjectType): + class Meta: + type_name_prefix = "" + + field = String() + my_union = MyUnion() + my_bar_type = Field(MyBarType) + + class Query(ObjectType): + class Meta: + type_name_prefix = "OverridePrefix" + + inner = Field(MyType) + + class CreateUser(Mutation_): + class Meta: + type_name_prefix = "" + + class Arguments: + name = String() + + name = String() + + def mutate(self, info, name): + return CreateUser(name=name) + + class Mutation(ObjectType): + class Meta: + type_name_prefix = "" + + create_user = CreateUser.Field() + + class Subscription(ObjectType): + class Meta: + type_name_prefix = "" + + count_to_ten = Field(Int) + + schema = Schema( + Query, + Mutation, + Subscription, + auto_camelcase=True, + type_name_prefix="MyPrefix", + ) + assert ( + str(schema).strip() + == dedent( + """ + type Query { + overridePrefixInner: MyType + } + + type MyType { + field: String + myUnion: MyUnion + myBarType: MyBarType + } + + union MyUnion = MyBarType | MyFooType + + type MyBarType { + field(input: OverridePrefixMyInputObjectType): String + myInterface: MyInterface + myScalar: MyScalar + myEnum: MyEnum + } + + input OverridePrefixMyInputObjectType { + field: String + } + + interface MyInterface { + field: String + } + + scalar MyScalar + + enum MyEnum { + FOO + BAR + } + + type MyFooType { + field: String + } + + type Mutation { + createUser(name: String): CreateUser + } + + type CreateUser { + name: String + } + + type Subscription { + countToTen: Int + } + """ + ).strip() + ) + + +class MyInputObjectType(InputObjectType): + field = String() + + +class MyScalar(Scalar): + ... + + +class MyEnum(Enum): + FOO = "foo" + BAR = "bar" + + +class MyInterface(Interface): + field = String() + + +class MyBarType(ObjectType): + field = String(input=MyInputObjectType()) + my_interface = Field(MyInterface) + my_scalar = MyScalar() + my_enum = MyEnum() + + +class MyFooType(ObjectType): + field = String() + + +class MyUnion(Union): + class Meta: + types = (MyBarType, MyFooType) + + +class MyType(ObjectType): + field = String() + my_union = MyUnion() + my_bar_type = Field(MyBarType) + + +class Query(ObjectType): + inner = Field(MyType) + + +class CreateUser(Mutation_): + class Arguments: + name = String() + + name = String() + + def mutate(self, info, name): + return CreateUser(name=name) + + +class Mutation(ObjectType): + create_user = CreateUser.Field() + + +class Subscription(ObjectType): + count_to_ten = Field(Int) + + +def test_schema_type_name_prefix_camelcase(): + + schema = Schema( + Query, + Mutation, + Subscription, + auto_camelcase=True, + type_name_prefix="MyPrefix", + ) + assert ( + str(schema).strip() + == dedent( + """ + type Query { + myPrefixInner: MyPrefixMyType + } + + type MyPrefixMyType { + field: String + myUnion: MyPrefixMyUnion + myBarType: MyPrefixMyBarType + } + + union MyPrefixMyUnion = MyPrefixMyBarType | MyPrefixMyFooType + + type MyPrefixMyBarType { + field(input: MyPrefixMyInputObjectType): String + myInterface: MyPrefixMyInterface + myScalar: MyPrefixMyScalar + myEnum: MyPrefixMyEnum + } + + input MyPrefixMyInputObjectType { + field: String + } + + interface MyPrefixMyInterface { + field: String + } + + scalar MyPrefixMyScalar + + enum MyPrefixMyEnum { + FOO + BAR + } + + type MyPrefixMyFooType { + field: String + } + + type Mutation { + myPrefixCreateUser(name: String): MyPrefixCreateUser + } + + type MyPrefixCreateUser { + name: String + } + + type Subscription { + myPrefixCountToTen: Int + } + """ + ).strip() + ) + + +def test_schema_type_name_prefix_camelcase_disabled(): + schema = Schema( + Query, + Mutation, + Subscription, + auto_camelcase=False, + type_name_prefix="MyPrefix", + ) + assert ( + str(schema).strip() + == dedent( + """ + type Query { + MyPrefixinner: MyPrefixMyType + } + + type MyPrefixMyType { + field: String + my_union: MyPrefixMyUnion + my_bar_type: MyPrefixMyBarType + } + + union MyPrefixMyUnion = MyPrefixMyBarType | MyPrefixMyFooType + + type MyPrefixMyBarType { + field(input: MyPrefixMyInputObjectType): String + my_interface: MyPrefixMyInterface + my_scalar: MyPrefixMyScalar + my_enum: MyPrefixMyEnum + } + + input MyPrefixMyInputObjectType { + field: String + } + + interface MyPrefixMyInterface { + field: String + } + + scalar MyPrefixMyScalar + + enum MyPrefixMyEnum { + FOO + BAR + } + + type MyPrefixMyFooType { + field: String + } + + type Mutation { + MyPrefixcreate_user(name: String): MyPrefixCreateUser + } + + type MyPrefixCreateUser { + name: String + } + + type Subscription { + MyPrefixcount_to_ten: Int + } + """ + ).strip() + )