Exemplo n.º 1
0
def merge_types(name: str, types: Tuple[type, ...]) -> type:
    """Merge multiple Strawberry types into one

    For example, given two queries `A` and `B`, one can merge them into a
    super type as follows:

        merge_types("SuperQuery", (B, A))

    This is essentially the same as:

        class SuperQuery(B, A):
            ...
    """

    if not types:
        raise ValueError("Can't merge types if none are supplied")

    fields = chain(*(t._type_definition.fields
                     for t in types)  # type: ignore[attr-defined]
                   )
    counter = Counter(f.name for f in fields)
    dupes = [f for f, c in counter.most_common() if c > 1]
    if dupes:
        warnings.warn("{} has overridden fields: {}".format(
            name, ", ".join(dupes)))

    return strawberry.type(type(name, types, {}))
Exemplo n.º 2
0
def create_type(name: str, fields: List[StrawberryField]) -> Type:
    """Create a Strawberry type from a list of StrawberryFields

    >>> @strawberry.field
    >>> def hello(info) -> str:
    >>>     return "World"
    >>>
    >>> Query = create_type(name="Query", fields=[hello])
    """

    if not fields:
        raise ValueError(f'Can\'t create type "{name}" with no fields')

    namespace = {}
    annotations = {}

    for field in fields:
        if not isinstance(field, StrawberryField):
            raise TypeError("Field is not an instance of StrawberryField")

        if field.python_name is None:
            raise ValueError(
                ("Field doesn't have a name. Fields passed to "
                 "`create_type` must define a name by passing the "
                 "`name` argument to `strawberry.field`."))

        namespace[field.python_name] = field
        annotations[field.python_name] = field.type

    namespace["__annotations__"] = annotations  # type: ignore

    cls = types.new_class(name, (), {}, lambda ns: ns.update(namespace))

    return strawberry.type(cls)
Exemplo n.º 3
0
def create_type(name: str, fields: List[StrawberryField]) -> Type:
    """Create a Strawberry type from a list of StrawberryFields

    >>> @strawberry.field
    >>> def hello(info) -> str:
    >>>     return "World"
    >>>
    >>> Query = create_type(name="Query", fields=[hello])
    """

    if len(fields) == 0:
        raise ValueError(f'Can\'t create type "{name}" with no fields')

    dataclass_fields = []

    for field in fields:
        if not isinstance(field, StrawberryField):
            raise TypeError("Field is not an instance of StrawberryField")

        if field.graphql_name is None:
            raise ValueError(
                ("Field doesn't have a name. Fields passed to "
                 "`create_type` must define a name by passing the "
                 "`name` argument to `strawberry.field`."))

        dataclass_fields.append((
            field.graphql_name,
            field.type,
            dataclasses_field(default=field),
        ))

    cls = make_dataclass(name, fields=dataclass_fields)

    return strawberry.type(cls)
Exemplo n.º 4
0
 def mutation(cls):
     object_name = utils.camel_to_snake(cls.model._meta.object_name)
     class Mutation: pass
     setattr(Mutation, f'create_{object_name}', cls.create_mutation())
     setattr(Mutation, f'create_{object_name}s', cls.batch_create_mutation())
     setattr(Mutation, f'update_{object_name}s', cls.update_mutation())
     setattr(Mutation, f'delete_{object_name}s', cls.delete_mutation())
     return strawberry.type(Mutation)
Exemplo n.º 5
0
def create_error_type(base_name, graphql_fields):
    fields = [(name, List[str], dataclasses.field(default_factory=list))
              for name, _ in graphql_fields.items()]
    fields.append(
        ("nonFieldErrors", List[str], dataclasses.field(default_factory=list)))

    error_type = dataclasses.make_dataclass(f"{base_name}Errors", fields)
    return strawberry.type(error_type)
Exemplo n.º 6
0
def generate_model_type(resolver_cls, is_input=False, is_update=False):
    model = resolver_cls.model
    annotations = {}
    attributes = {'__annotations__': annotations}

    # add fields
    for field in model._meta.get_fields():
        if resolver_cls.fields and field.name not in resolver_cls.fields:
            continue  # skip
        if is_in(field.name, resolver_cls.exclude):
            continue  # skip
        if is_input and is_in(field.name, resolver_cls.readonly_fields):
            continue  # skip
        if field.is_relation:
            if isinstance(field, fields.related.ForeignKey):
                field_params = get_relation_foreignkey_field(
                    field, is_input, is_update)
            else:
                if is_input:
                    continue
                field_params = get_relation_field(field)
        else:
            field_params = get_field(field, is_input, is_update)
        if not field_params:
            continue

        field_name, field_type, field_kwargs = field_params

        if is_input:
            attributes[field_name] = strawberry.arguments.UNSET
        else:
            if resolver_cls.field_permission_classes:
                field_kwargs[
                    'permission_classes'] = resolver_cls.field_permission_classes
            attributes[field_name] = strawberry.field(**field_kwargs)

        if field_type:
            annotations[field_name] = field_type

    if not is_input:
        for field_name in dir(resolver_cls):
            field = getattr(resolver_cls, field_name)
            if hasattr(field, '_field_definition'):
                attributes[field_name] = field

    # generate type
    type_name = model._meta.object_name
    if is_update:
        type_name = f'Update{type_name}'
    elif is_input:
        type_name = f'Create{type_name}'
    model_type = type(type_name, (), attributes)
    model_type = strawberry.type(model_type, is_input=is_input)
    if not is_input:
        register_model_type(model, model_type)
        register_resolver_cls(model, resolver_cls)
    return model_type
Exemplo n.º 7
0
def create_mutation_type(name: str, mutations: List[StrawberryField]):
    cls = make_dataclass(
        name,
        fields=[(
            mutation._field_definition.origin_name,
            mutation._field_definition.type,
            field(default=strawberry.mutation(
                mutation._field_definition.origin)),
        ) for mutation in mutations],
    )

    return strawberry.type(cls, name=name)
Exemplo n.º 8
0
def create_validation_error_type(prefix: str, type_: StrawberryField):
    @classmethod
    def from_validation_error(cls, validation_error: pydantic.ValidationError):
        errors = validation_error.errors()
        payload = {}

        for error in errors:
            field = error["loc"][0]
            message = error["msg"]
            error_type = error["type"]

            errors = payload.setdefault(field, [])
            errors.append(FieldError(message=message, type=error_type))

        return cls(errors=type_(**payload))

    cls = make_dataclass(f"{prefix}ValidationError", [("errors", type_)])
    cls.from_validation_error = from_validation_error

    return strawberry.type(cls)
Exemplo n.º 9
0
def test_create_type_field_invalid():
    with pytest.raises(TypeError):
        create_type("MyType", [strawberry.type()])
Exemplo n.º 10
0
 def query(cls):
     object_name = utils.camel_to_snake(cls.model._meta.object_name)
     class Query: pass
     setattr(Query, f'{object_name}', cls.get_field())
     setattr(Query, f'{object_name}s', cls.list_field())
     return strawberry.type(Query)
Exemplo n.º 11
0
def obj(cls):
    return strawberry.type(cls)
Exemplo n.º 12
0
def graphql(cls):
    if Enum in cls.__mro__:
        return strawberry.enum(cls)
    return strawberry.type(cls)