Ejemplo n.º 1
0
    def check_union_name(self, ann: types.AUnion) -> None:
        """Raise SchemaError if `ann` does not have a proper name."""
        name = ann.name

        from graphql.utils.assert_valid_name import COMPILED_NAME_PATTERN
        if name is None or not isinstance(
                name, str) or not COMPILED_NAME_PATTERN.match(name):
            args = [types.type_repr(of_t.t) for of_t in ann.of_types]
            defined_at = f"Defined at {ann.origin.classname}.{ann.origin.fieldname}.\n" if ann.origin else ""
            raise SchemaError(f"""Could not find a name for Union{args}.
{defined_at}
In GraphQL, any union needs a name, so all unions must be
forward-referenced, e.g.:
    Person = Union[Manager, Employee]
    def person(self) -> Optional['Person']: ...
""")
Ejemplo n.º 2
0
def check_type_name_is_valid(name):
    """Check if input is a valid, nonreserved GraphQL type name.

    Args:
        name: str

    Raises:
        - InvalidTypeNameError if the name doesn't consist of only alphanumeric characters and
          underscores, starts with a numeric character, or starts with double underscores
    """
    if not isinstance(name, str):
        raise InvalidTypeNameError(u'Name "{}" is not a string.'.format(name))
    if not COMPILED_NAME_PATTERN.match(name):
        raise InvalidTypeNameError(
            u'"{}" is not a valid GraphQL name.'.format(name))
    if name.startswith('__'):
        raise InvalidTypeNameError(
            u'"{}" starts with two underscores, which is reserved for '
            u'GraphQL internal use and is not allowed.'.format(name))
def _get_fields_for_class(schema_graph, graphql_types, field_type_overrides,
                          hidden_classes, cls_name):
    """Return a dict from field name to GraphQL field type, for the specified graph class."""
    properties = schema_graph.get_element_by_class_name(cls_name).properties

    # Add leaf GraphQL fields (class properties).
    all_properties = {}
    for property_name, property_obj in six.iteritems(properties):
        if COMPILED_NAME_PATTERN.match(property_name):
            all_properties[property_name] = property_obj.type
        else:
            warnings.warn(
                u'Ignoring property {} of class {} with invalid name. '
                u'Property names must match /{}/.'.format(
                    property_name, cls_name, COMPILED_NAME_PATTERN))

    collections_of_non_graphql_scalars = {
        property_name
        for property_name, graphql_type in six.iteritems(all_properties)
        if (isinstance(strip_non_null_from_type(graphql_type), GraphQLList)
            and not isinstance(strip_non_null_from_type(graphql_type.of_type),
                               GraphQLScalarType))
    }

    if len(collections_of_non_graphql_scalars) > 0:
        warnings.warn(
            'The fields {} of class {} were ignored since they are GraphQLLists of '
            'non-GraphQLScalarTypes. GraphQLLists of non-GraphQLScalarTypes are not '
            'currently supported in the GraphQLSchema.'.format(
                collections_of_non_graphql_scalars, cls_name))

    # Filter collections of non-GraphQLScalarTypes. They are currently not supported.
    result = {
        property_name: graphql_type
        for property_name, graphql_type in six.iteritems(all_properties)
        if property_name not in collections_of_non_graphql_scalars
    }

    # Add edge GraphQL fields.
    schema_element = schema_graph.get_element_by_class_name(cls_name)
    outbound_edges = (('out_{}'.format(out_edge_name),
                       schema_graph.get_element_by_class_name(
                           out_edge_name).base_out_connection)
                      for out_edge_name in schema_element.out_connections)
    inbound_edges = (('in_{}'.format(in_edge_name),
                      schema_graph.get_element_by_class_name(
                          in_edge_name).base_in_connection)
                     for in_edge_name in schema_element.in_connections)
    for field_name, to_type_name in chain(outbound_edges, inbound_edges):
        edge_endpoint_type_name = None
        subclasses = schema_graph.get_subclass_set(to_type_name)

        to_type_abstract = schema_graph.get_element_by_class_name(
            to_type_name).abstract
        if not to_type_abstract and len(subclasses) > 1:
            # If the edge endpoint type has no subclasses, it can't be coerced into any other
            # type. If the edge endpoint type is abstract (an interface type), we can already
            # coerce it to the proper type with a GraphQL fragment. However, if the endpoint
            # type is non-abstract and has subclasses, we need to return its subclasses as an
            # union type. This is because GraphQL fragments cannot be applied on concrete
            # types, and GraphQL does not support inheritance of concrete types.
            type_names_to_union = [
                subclass for subclass in subclasses
                if subclass not in hidden_classes
            ]
            if type_names_to_union:
                edge_endpoint_type_name = _get_union_type_name(
                    type_names_to_union)
        else:
            if to_type_name not in hidden_classes:
                edge_endpoint_type_name = to_type_name

        if edge_endpoint_type_name is not None:
            # If we decided to not hide this edge due to its endpoint type being
            # non-representable, represent the edge field as the GraphQL type
            # List(edge_endpoint_type_name).
            result[field_name] = GraphQLList(
                graphql_types[edge_endpoint_type_name])

    for field_name, field_type in six.iteritems(field_type_overrides):
        if field_name not in result:
            raise AssertionError(
                u'Attempting to override field "{}" from class "{}", but the '
                u'class does not contain said field'.format(
                    field_name, cls_name))
        else:
            result[field_name] = field_type

    return result