diff --git a/hiku/validate/graph.py b/hiku/validate/graph.py index 70b35ef..b816201 100644 --- a/hiku/validate/graph.py +++ b/hiku/validate/graph.py @@ -20,11 +20,16 @@ ) from ..graph import AbstractNode, AbstractField, AbstractLink, AbstractOption +from ..compat import Protocol from .errors import Errors from ..scalar import Scalar from ..types import GenericMeta, OptionalMeta +class DescriptionProtocol(Protocol): + description: t.Optional[str] + + class GraphValidationError(TypeError): def __init__(self, errors: t.List[str]) -> None: self.errors = errors @@ -134,6 +139,15 @@ def _validate_deprecated_duplicates( ) ) + def _validate_description(self, obj: DescriptionProtocol) -> None: + if obj.description is not None: + if not isinstance(obj.description, str): + self.errors.report( + '{} "{}" description must be a string'.format( + obj.__class__.__name__, self._format_path(obj) + ), + ) + def visit_option(self, obj: Option) -> None: if ( isinstance(obj.type, GenericMeta) @@ -146,6 +160,8 @@ def visit_option(self, obj: Option) -> None: ), ) + self._validate_description(obj) + def visit_field(self, obj: Field) -> None: invalid = [ f @@ -186,6 +202,7 @@ def visit_field(self, obj: Field) -> None: ) return + self._validate_description(obj) self._validate_deprecated_duplicates(obj) def visit_link(self, obj: Link) -> None: @@ -230,6 +247,7 @@ def visit_link(self, obj: Link) -> None: ) ) + self._validate_description(obj) self._validate_deprecated_duplicates(obj) def visit_node(self, obj: Node) -> None: @@ -269,6 +287,8 @@ def visit_node(self, obj: Node) -> None: ) ) + self._validate_description(obj) + def visit_union(self, obj: "Union") -> t.Any: if not obj.name: self.errors.report("Union must have a name") @@ -288,6 +308,8 @@ def visit_union(self, obj: "Union") -> t.Any: ) return + self._validate_description(obj) + def visit_interface(self, obj: "Interface") -> t.Any: if not obj.name: self.errors.report("Interface must have a name") @@ -311,6 +333,8 @@ def visit_interface(self, obj: "Interface") -> t.Any: ) return + self._validate_description(obj) + def visit_enum(self, obj: BaseEnum) -> t.Any: if not obj.name: self.errors.report("Enum must have a name") @@ -320,6 +344,8 @@ def visit_enum(self, obj: BaseEnum) -> t.Any: self.errors.report("Enum must have at least one value") return + self._validate_description(obj) + def visit_scalar(self, obj: t.Type[Scalar]) -> t.Any: ... diff --git a/tests/test_validate_graph.py b/tests/test_validate_graph.py index e33a178..8b6cf8b 100644 --- a/tests/test_validate_graph.py +++ b/tests/test_validate_graph.py @@ -283,3 +283,12 @@ def test_link_uses_more_than_one_deprecated_directive(): ], ['Deprecated directive must be used only once for "bar.baz", found 2'], ) + + +def test_graph_contain_duplicate_nodes(): + check_errors( + [ + Node("foo", [], description=tuple("foo")), + ], + ['Node "foo" description must be a string'], + ) \ No newline at end of file