def validate(self) -> bool: """ Check that the given schema is valid. :return: bool """ # TODO: Optimization: most validation functions iterate over # the schema types: it could be done in one loop. validators = [ self._validate_schema_named_types, self._validate_object_follow_interfaces, self._validate_schema_root_types_exist, self._validate_non_empty_object, self._validate_union_is_acceptable, self._validate_all_scalars_have_implementations, self._validate_enum_values_are_unique, self._validate_arguments_have_valid_type, self._validate_input_type_composed_of_input_type, self._validate_directive_implementation # TODO: Validate Field: default value must be of given type # TODO: Check all objects have resolvers (at least in parent) ] errors = [] for validator in validators: errors.extend(validator()) if errors: raise GraphQLSchemaError( message=_format_schema_error_message(errors)) return True
def _validate_schema_root_types_exist(self) -> bool: # Check "query" which is the only mandatory root type if self.query_type not in self._gql_types: raise GraphQLSchemaError( "schema could not find the root `query` type `{}`.".format( self.query_type)) if (self.mutation_type != "Mutation" and self.mutation_type not in self._gql_types): raise GraphQLSchemaError( "schema could not find the root `mutation` type `{}`.".format( self.mutation_type)) if (self.subscription_type != "Subscription" and self.subscription_type not in self._gql_types): raise GraphQLSchemaError( "schema could not find the root `subscription` type `{}`.". format(self.subscription_type)) return True
def _validate_non_empty_object(self): for type_name, gql_type in self._gql_types.items(): if isinstance(gql_type, GraphQLObjectType) and not gql_type.fields: raise GraphQLSchemaError( "object type `{}` has no fields.".format( type_name ) ) return True
def _validate_all_scalars_have_implementations(self) -> bool: for type_name, gql_type in self._gql_types.items(): if isinstance(gql_type, GraphQLScalarType): if (gql_type.coerce_output is None or gql_type.coerce_input is None): raise GraphQLSchemaError( "scalar type `{}` must have a coercion " "function for inputs and outputs.".format(type_name)) return True
def _validate_enum_values_are_unique(self) -> bool: for type_name, gql_type in self._gql_types.items(): if isinstance(gql_type, GraphQLEnumType): for value in gql_type.values: if str(value.value) in self._gql_types: raise GraphQLSchemaError( "enum type `{}` has a value of `{}` which " "is not unique in the GraphQL schema.".format( type_name, str(value.value))) return True
def _validate_object_follow_interfaces(self): for type_name, gql_type in self._gql_types.items(): try: for iface_name in gql_type.interfaces: try: iface_type = self._gql_types[iface_name] except KeyError: raise GraphQLSchemaError( "GraphQL type `{}` implements the `{}` interface " "which does not exist!".format( gql_type.name, iface_name ) ) if not isinstance(iface_type, GraphQLInterfaceType): raise GraphQLSchemaError( "GraphQL type `{}` implements the `{}` interface " "which is not an interface!".format( gql_type.name, iface_name, ) ) for iface_field_name, iface_field in iface_type.fields.items(): try: gql_type_field = gql_type.fields[iface_field_name] except KeyError: raise GraphQLSchemaError( "field `{}` is missing in GraphQL type `{}` " "that implements the `{}` interface.".format( iface_field_name, gql_type.name, iface_name, ) ) if gql_type_field.gql_type != iface_field.gql_type: raise GraphQLSchemaError( "field `{}` in GraphQL type `{}` that " "implements the `{}` interface does not follow " "the interface field type `{}`.".format( iface_field_name, gql_type.name, iface_name, iface_field.gql_type ) ) except (AttributeError, TypeError): pass return True
def _validate_union_is_acceptable(self) -> bool: for type_name, gql_type in self._gql_types.items(): if isinstance(gql_type, GraphQLUnionType): for contained_type_name in gql_type.gql_types: if contained_type_name == type_name: raise GraphQLSchemaError( "union type `{}` contains itself.".format( type_name)) # TODO: Are there other restrictions for `Union`s ? # can they contain interfaces ? # can they mix types: interface | object | scalar return True
def _validate_schema_named_types(self) -> bool: for type_name, gql_type in self._gql_types.items(): try: for field in gql_type.fields: reduced_type = reduce_type(field.gql_type) if str(reduced_type) not in self._gql_types: raise GraphQLSchemaError( "field `{}` in GraphQL type `{}` is invalid, " "the given type `{}` does not exist!".format( field.name, type_name, reduced_type)) except AttributeError: pass return True
def _validate_extensions(self) -> None: validators = [ self._validate_enum_extensions, self._validate_input_object_extensions, self._validate_object_extensions, self._validate_interface_extensions, self._validate_scalar_extensions, self._validate_union_extensions, self._validate_schema_extensions, ] errors = [] for validator in validators: errors.extend(validator()) if errors: raise GraphQLSchemaError( message=_format_schema_error_message(errors))