Esempio n. 1
0
def _get_entity_type(type_map: TypeMap):
    # https://www.apollographql.com/docs/apollo-server/federation/federation-spec/#resolve-requests-for-entities

    # To implement the _Entity union, each type annotated with @key
    # should be added to the _Entity union.

    federation_key_types = [
        type.implementation for type in type_map.values()
        if _has_federation_keys(type.definition)
    ]

    # If no types are annotated with the key directive, then the _Entity
    # union and Query._entities field should be removed from the schema.
    if not federation_key_types:
        return None

    entity_type = GraphQLUnionType("_Entity",
                                   federation_key_types)  # type: ignore

    def _resolve_type(self, value, _type):
        return type_map[self._type_definition.name].implementation

    entity_type.resolve_type = _resolve_type

    return entity_type
Esempio n. 2
0
def union(name: str, types: typing.Tuple[typing.Type], *, description=None):
    """Creates a new named Union type.

    Example usages:

    >>> strawberry.union(
    >>>     "Name",
    >>>     (A, B),
    >>> )

    >>> strawberry.union(
    >>>     "Name",
    >>>     (A, B),
    >>> )
    """

    from .type_converter import get_graphql_type_for_annotation

    def _resolve_type(root, info, _type):
        if not hasattr(root, "graphql_type"):
            raise WrongReturnTypeForUnion(info.field_name, str(type(root)))

        if is_generic(type(root)):
            return _find_type_for_generic_union(root)

        if root.graphql_type not in _type.types:
            raise UnallowedReturnTypeForUnion(info.field_name, str(type(root)),
                                              _type.types)

        return root.graphql_type

    # TODO: union types don't work with scalar types
    # so we want to return a nice error
    # also we want to make sure we have been passed
    # strawberry types
    graphql_type = GraphQLUnionType(
        name,
        [
            get_graphql_type_for_annotation(type, name, force_optional=True)
            for type in types
        ],
        description=description,
    )
    graphql_type.resolve_type = _resolve_type

    # This is currently a temporary solution, this is ok for now
    # But in future we might want to change this so that it works
    # properly with mypy, but there's no way to return a type like NewType does
    # so we return this class instance as it allows us to reuse the rest of
    # our code without doing too many changes

    class X:
        def __init__(self, graphql_type):
            self.graphql_type = graphql_type

        def __call__(self):
            raise ValueError("Cannot use union type directly")

    return X(graphql_type)
Esempio n. 3
0
def get_graphql_type_for_annotation(annotation,
                                    field_name: str,
                                    force_optional: bool = False):
    # TODO: this might lead to issues with types that have a field value
    is_field_optional = force_optional

    if hasattr(annotation, "field"):
        graphql_type = annotation.field
    else:
        annotation_name = getattr(annotation, "_name", None)

        if annotation_name == "List":
            list_of_type = get_graphql_type_for_annotation(
                annotation.__args__[0], field_name)

            return GraphQLList(list_of_type)

        annotation_origin = getattr(annotation, "__origin__", None)

        if annotation_origin == AsyncGenerator:
            # async generators are used in subscription, we only need the yield type
            # https://docs.python.org/3/library/typing.html#typing.AsyncGenerator
            return get_graphql_type_for_annotation(annotation.__args__[0],
                                                   field_name)

        elif is_union(annotation):
            types = annotation.__args__
            non_none_types = [x for x in types
                              if x != None.__class__]  # noqa:E721

            # optionals are represented as Union[type, None]
            if len(non_none_types) == 1:
                is_field_optional = True
                graphql_type = get_graphql_type_for_annotation(
                    non_none_types[0], field_name, force_optional=True)
            else:
                is_field_optional = None.__class__ in types

                def _resolve_type(self, value, _type):
                    if not hasattr(self, "field"):
                        raise WrongReturnTypeForUnion(value.field_name,
                                                      str(type(self)))

                    if self.field not in _type.types:
                        raise UnallowedReturnTypeForUnion(
                            value.field_name, str(type(self)), _type.types)

                    return self.field

                # TODO: union types don't work with scalar types
                # so we want to return a nice error
                # also we want to make sure we have been passed
                # strawberry types
                graphql_type = GraphQLUnionType(field_name,
                                                [type.field for type in types])
                graphql_type.resolve_type = _resolve_type
        else:
            graphql_type = REGISTRY.get(annotation)

    if not graphql_type:
        raise ValueError(f"Unable to get GraphQL type for {annotation}")

    if is_field_optional:
        return graphql_type

    return GraphQLNonNull(graphql_type)