Ejemplo n.º 1
0
def is_valid_nonreserved_name(name: str) -> bool:
    """Check if input is a valid, non-reserved GraphQL name.

    A GraphQL name is valid iff it consists of only alphanumeric characters and underscores and
    does not start with a numeric character. It is non-reserved (i.e. not reserved for GraphQL
    internal use) if it does not start with double underscores.

    Args:
        name: to be checked

    Returns:
        True iff name is a valid, non-reserved GraphQL type name.
    """
    return bool(re_name.match(name)) and not name.startswith("__")
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('Name "{}" is not a string.'.format(name))
    if not re_name.match(name):
        raise InvalidTypeNameError('"{}" is not a valid GraphQL name.'.format(name))
    if name.startswith("__"):
        raise InvalidTypeNameError(
            '"{}" starts with two underscores, which is reserved for '
            "GraphQL internal use and is not allowed.".format(name)
        )
Ejemplo n.º 3
0
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 re_name.match(property_name):
            all_properties[property_name] = property_obj.type
        else:
            warnings.warn(
                "Ignoring property {} of class {} with invalid name. "
                "Property names must match /{}/.".format(property_name, cls_name, re_name)
            )

    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(
                'Attempting to override field "{}" from class "{}", but the '
                "class does not contain said field".format(field_name, cls_name)
            )
        else:
            result[field_name] = field_type

    return result