diff --git a/graphlayer/graphql/schema.py b/graphlayer/graphql/schema.py index f415d50..690c639 100644 --- a/graphlayer/graphql/schema.py +++ b/graphlayer/graphql/schema.py @@ -51,6 +51,7 @@ def generate_graphql_type(graph_type): (snake_case_to_camel_case(field.name), to_graphql_input_field(field)) for field in graph_type.fields ), + description=graph_type.description, )) elif isinstance(graph_type, schema.InterfaceType): @@ -58,6 +59,7 @@ def generate_graphql_type(graph_type): name=graph_type.name, fields=to_graphql_fields(graph_type.fields), resolve_type=lambda: None, + description=graph_type.description, )) elif isinstance(graph_type, schema.ListType): @@ -74,6 +76,7 @@ def generate_graphql_type(graph_type): to_graphql_type(interface).of_type for interface in graph_type.interfaces ), + description=graph_type.description, )) else: @@ -85,7 +88,7 @@ def to_graphql_input_field(graph_field): if graph_field.has_default and isinstance(graphql_type, graphql.GraphQLNonNull): graphql_type = graphql_type.of_type - return graphql.GraphQLInputField(type_=graphql_type) + return graphql.GraphQLInputField(type_=graphql_type, description=graph_field.description) def to_graphql_fields(graph_fields): return lambda: iterables.to_dict( @@ -100,6 +103,7 @@ def to_graphql_field(graph_field): (snake_case_to_camel_case(param.name), to_graphql_argument(param)) for param in graph_field.params ), + description=graph_field.description, ) def to_graphql_argument(param): @@ -108,7 +112,7 @@ def to_graphql_argument(param): if param.has_default and isinstance(graphql_type, graphql.GraphQLNonNull): graphql_type = graphql_type.of_type - return graphql.GraphQLArgument(type_=graphql_type) + return graphql.GraphQLArgument(type_=graphql_type, description=param.description) graphql_query_type = to_graphql_type(query_type).of_type if mutation_type is None: diff --git a/graphlayer/schema.py b/graphlayer/schema.py index 116a6f3..45a236f 100644 --- a/graphlayer/schema.py +++ b/graphlayer/schema.py @@ -145,10 +145,11 @@ def __str__(self): class InputObjectType(object): - def __init__(self, name, fields): + def __init__(self, name, fields, description=None): self.name = name self.fields = Fields(name, fields) self.instance_type = memoize(self._create_instance_type) + self.description = description def _create_instance_type(self): name = self.name @@ -222,15 +223,16 @@ def coerce(self, value): raise _coercion_error(value, self.name) -def input_field(name, type, default=_undefined): - return InputField(name, type, default) +def input_field(name, type, default=_undefined, description=None): + return InputField(name, type, default, description=description) class InputField(object): - def __init__(self, name, type, default): + def __init__(self, name, type, default, description=None): self.name = name self.type = type self.default = default + self.description = description @property def has_default(self): @@ -241,9 +243,10 @@ def __repr__(self): class InterfaceType(object): - def __init__(self, name, fields): + def __init__(self, name, fields, description=None): self.name = name self.fields = Fields(name, fields) + self.description = description def __call__(self, *field_queries): return ObjectQuery.create(self, field_queries=field_queries) @@ -399,11 +402,12 @@ def __str__(self): class ObjectType(object): - def __init__(self, name, fields, interfaces=None): + def __init__(self, name, fields, interfaces=None, description=None): if interfaces is None: interfaces = () self.name = name + self.description = description if not callable(fields): fields = lambdaize(fields) def owned_fields(): @@ -579,21 +583,28 @@ class Args(object): pass -def field(name, type, params=None): +def field(name, type, params=None, description=None): if params is None: params = () - return Field(owner_type=None, name=name, type=type, params=params) + return Field(owner_type=None, name=name, type=type, params=params, description=description) class Field(object): - def __init__(self, owner_type, name, type, params): + def __init__(self, owner_type, name, type, params, description=None): self.owner_type = owner_type self.name = name self.type = type self.params = Params(name, params) + self.description = description def with_owner_type(self, owner_type): - return Field(owner_type=owner_type, name=self.name, type=self.type, params=self.params) + return Field( + owner_type=owner_type, + name=self.name, + type=self.type, + params=self.params, + description=self.description, + ) def __call__(self, *args): field_queries, field_args = _partition_by_type(args, (FieldQuery, Argument)) @@ -731,15 +742,16 @@ def key(key, field_query): ) -def param(name, type, default=_undefined): - return Parameter(name=name, type=type, default=default) +def param(name, type, default=_undefined, description=None): + return Parameter(name=name, type=type, default=default, description=description) class Parameter(object): - def __init__(self, name, type, default): + def __init__(self, name, type, default, description=None): self.name = name self.type = type self.default = default + self.description = description @property def has_default(self): diff --git a/setup.py b/setup.py index f969fd1..8cee992 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ def read(fname): setup( name='graphlayer', - version='0.4.0', + version='0.5.0', description='High-performance library for implementing GraphQL APIs', long_description=read("README.rst"), author='Michael Williamson', diff --git a/tests/graphql/test_schema.py b/tests/graphql/test_schema.py index f1eef6f..f115a8d 100644 --- a/tests/graphql/test_schema.py +++ b/tests/graphql/test_schema.py @@ -86,6 +86,82 @@ def test_object_type_is_converted_to_non_null_graphql_object_type(): )) +def test_object_type_description_is_converted_to_graphql_object_type_description(): + graph_type = g.ObjectType("Obj", description="An object.", fields=( + g.field("value", type=g.String), + )) + + assert_that(to_graphql_type(graph_type), is_graphql_non_null( + is_graphql_object_type(description=equal_to("An object.")), + )) + + +def test_field_description_is_converted_to_graphql_field_description(): + graph_type = g.ObjectType("Obj", fields=( + g.field("value", type=g.String, description="A value."), + )) + + assert_that(to_graphql_type(graph_type), is_graphql_non_null( + is_graphql_object_type( + fields=is_mapping({ + "value": is_graphql_field(description=equal_to("A value.")), + }), + ), + )) + + +def test_param_description_is_converted_to_graphql_argument_description(): + graph_type = g.ObjectType("Obj", fields=( + g.field("value", type=g.String, params=( + g.param("arg", g.Int, description="An argument."), + )), + )) + + assert_that(to_graphql_type(graph_type), is_graphql_non_null( + is_graphql_object_type( + fields=is_mapping({ + "value": is_graphql_field(args=is_mapping({ + "arg": is_graphql_argument(description=equal_to("An argument.")), + })), + }), + ), + )) + + +def test_interface_type_description_is_converted_to_graphql_interface_type_description(): + graph_type = g.InterfaceType("Item", description="An item.", fields=( + g.field("value", type=g.String), + )) + + assert_that(to_graphql_type(graph_type), is_graphql_non_null( + is_graphql_interface_type(name="Item", description=equal_to("An item.")), + )) + + +def test_input_object_type_description_is_converted_to_graphql_input_object_type_description(): + graph_type = g.InputObjectType("Obj", description="An input object.", fields=( + g.input_field("value", type=g.String), + )) + + assert_that(to_graphql_input_type(graph_type), is_graphql_non_null( + is_graphql_input_object_type(name="Obj", description=equal_to("An input object.")), + )) + + +def test_input_field_description_is_converted_to_graphql_input_field_description(): + graph_type = g.InputObjectType("Obj", fields=( + g.input_field("value", type=g.String, description="A value."), + )) + + assert_that(to_graphql_input_type(graph_type), is_graphql_non_null( + is_graphql_input_object_type( + fields=is_mapping({ + "value": is_graphql_input_field(description=equal_to("A value.")), + }), + ), + )) + + def test_object_type_field_names_are_converted_from_snake_case_to_camel_case(): graph_type = g.ObjectType("Obj", fields=( g.field("field_name", type=g.String), @@ -273,37 +349,48 @@ def is_graphql_enum_value(value): ) -def is_graphql_input_field(type): +def is_graphql_input_field(type=None, description=None): + if type is None: + type = anything + if description is None: + description = anything + return all_of( is_instance(graphql.GraphQLInputField), - has_attrs(type=type), + has_attrs(type=type, description=description), ) -def is_graphql_input_object_type(name=None, fields=None): +def is_graphql_input_object_type(name=None, fields=None, description=None): if name is None: name = anything if fields is None: fields = anything + if description is None: + description = anything return all_of( is_instance(graphql.GraphQLInputObjectType), has_attrs( name=name, fields=fields, + description=description, ), ) -def is_graphql_interface_type(name, fields=None): +def is_graphql_interface_type(name, fields=None, description=None): if fields is None: fields = anything + if description is None: + description = anything return all_of( is_instance(graphql.GraphQLInterfaceType), has_attrs( name=name, fields=fields, + description=description, ), ) @@ -322,7 +409,7 @@ def is_graphql_non_null(element_matcher): ) -def is_graphql_object_type(name=None, fields=None, interfaces=None): +def is_graphql_object_type(name=None, fields=None, interfaces=None, description=None): if name is None: name = anything @@ -332,35 +419,45 @@ def is_graphql_object_type(name=None, fields=None, interfaces=None): if interfaces is None: interfaces = anything + if description is None: + description = anything + return all_of( is_instance(graphql.GraphQLObjectType), has_attrs( name=name, fields=fields, interfaces=interfaces, + description=description, ), ) -def is_graphql_field(type=None, args=None): +def is_graphql_field(type=None, args=None, description=None): if type is None: type = anything if args is None: args = anything + if description is None: + description = anything + return all_of( is_instance(graphql.GraphQLField), - has_attrs(type=type, args=args), + has_attrs(type=type, args=args, description=description), ) -def is_graphql_argument(type=None): +def is_graphql_argument(type=None, description=None): if type is None: type = anything + if description is None: + description = anything + return all_of( is_instance(graphql.GraphQLArgument), - has_attrs(type=type), + has_attrs(type=type, description=description), )