Esempio n. 1
0
 def _field(self, field: ObjectField) -> Lazy[graphql.GraphQLInputField]:
     field_type = field.type
     field_default = graphql.Undefined if field.required else field.get_default(
     )
     default: Any = graphql.Undefined
     # Don't put `null` default + handle Undefined as None
     if field_default in {None, Undefined}:
         field_type = Optional[field_type]
     elif field_default is not graphql.Undefined:
         try:
             default = serialize(
                 field_type,
                 field_default,
                 aliaser=self.aliaser,
                 conversion=field.deserialization,
             )
         except Exception:
             field_type = Optional[field_type]
     factory = self.visit_with_conv(field_type, field.deserialization)
     return lambda: graphql.GraphQLInputField(
         factory.type,  # type: ignore
         default_value=default,
         description=get_description(field.schema, field.type),
         extensions={field.name: ""},
     )
Esempio n. 2
0
def attrs_fields(cls: type) -> Optional[Sequence[ObjectField]]:
    if hasattr(cls, "__attrs_attrs__"):
        return [
            ObjectField(a.name,
                        a.type,
                        required=a.default == attr.NOTHING,
                        default=a.default)
            for a in getattr(cls, "__attrs_attrs__")
        ]
    else:
        return prev_default_object_fields(cls)
Esempio n. 3
0
def column_field(name: str, column: Column) -> ObjectField:
    required = False
    default: Any = ...
    if column.default is not None:
        default = column.default
    elif column.server_default is not None:
        default = Undefined
    elif column.nullable:
        default = None
    else:
        required = True
    col_type = column.type.python_type
    col_type = Optional[col_type] if column.nullable else col_type
    return ObjectField(column.name or name,
                       col_type,
                       required,
                       default=default)
Esempio n. 4
0
 def visit_field(self, field: ObjectField) -> JsonSchema:
     result = full_schema(
         self.visit_with_conv(field.type, self._field_conversion(field)),
         field.schema,
     )
     if (not field.flattened and not field.pattern_properties
             and not field.additional_properties and not field.required
             and "default" not in result):
         result = JsonSchema(result)
         with suppress(Exception):
             result["default"] = serialize(
                 field.type,
                 field.get_default(),
                 fall_back_on_any=False,
                 check_type=True,
                 conversion=field.serialization,
             )
     return result
Esempio n. 5
0
    def _resolver(self, field: ResolverField) -> Lazy[graphql.GraphQLField]:
        resolve = self._wrap_resolve(
            resolver_resolve(
                field.resolver,
                field.types,
                self.aliaser,
                self.input_builder.default_conversion,
                self.default_conversion,
            ))
        args = None
        if field.parameters is not None:
            args = {}
            for param in field.parameters:
                default: Any = graphql.Undefined
                param_type = field.types[param.name]
                if is_union_of(param_type, graphql.GraphQLResolveInfo):
                    break
                param_field = ObjectField(
                    param.name,
                    param_type,
                    param.default is Parameter.empty,
                    field.metadata.get(param.name, empty_dict),
                    default=param.default,
                )
                if param_field.required:
                    pass
                # Don't put `null` default + handle Undefined as None
                # also https://github.com/python/typing/issues/775
                elif param.default in {None, Undefined}:
                    param_type = Optional[param_type]
                # param.default == graphql.Undefined means the parameter is required
                # even if it has a default
                elif param.default not in {Parameter.empty, graphql.Undefined}:
                    try:
                        default = serialize(
                            param_type,
                            param.default,
                            fall_back_on_any=False,
                            check_type=True,
                        )
                    except Exception:
                        param_type = Optional[param_type]
                arg_factory = self.input_builder.visit_with_conv(
                    param_type, param_field.deserialization)
                description = get_description(param_field.schema,
                                              param_field.type)

                def arg_thunk(
                        arg_factory=arg_factory,
                        default=default,
                        description=description) -> graphql.GraphQLArgument:
                    return graphql.GraphQLArgument(arg_factory.type, default,
                                                   description)

                args[self.aliaser(param_field.alias)] = arg_thunk
        factory = self.visit_with_conv(field.type, field.resolver.conversion)
        return lambda: graphql.GraphQLField(
            factory.type,  # type: ignore
            {name: arg()
             for name, arg in args.items()} if args else None,
            resolve,
            field.subscribe,
            field.description,
            field.deprecated,
        )
Esempio n. 6
0
from graphql import graphql_sync, print_schema
from pytest import raises

from apischema import deserialize, serialize
from apischema.graphql import graphql_schema
from apischema.json_schema import deserialization_schema, serialization_schema
from apischema.metadata import conversion, flattened
from apischema.objects import ObjectField, set_object_fields


class Field:
    def __init__(self, attr: int):
        self.attr = attr


set_object_fields(Field, [ObjectField("attr", int)])


@dataclass
class Data:
    data_field: Field = field(metadata=flattened)


json_schema = {
    "$schema":
    "http://json-schema.org/draft/2019-09/schema#",
    "type":
    "object",
    "allOf": [
        {
            "type": "object",
Esempio n. 7
0
from apischema import deserialize, serialize
from apischema.objects import ObjectField, set_object_fields


class Foo:
    def __init__(self, bar: int):
        self.bar = bar


set_object_fields(Foo, lambda: [ObjectField("bar", int, required=True)])

foo = deserialize(Foo, {"bar": 0})
assert type(foo) == Foo and foo.bar == 0
assert serialize(Foo, Foo(0)) == {"bar": 0}
Esempio n. 8
0
from apischema import deserialize, serialize
from apischema.json_schema import deserialization_schema
from apischema.objects import ObjectField, set_object_fields


class Foo:
    def __init__(self, bar):
        self.bar = bar


set_object_fields(Foo, [ObjectField("bar", int)])
# Fields can also be passed in a factory
set_object_fields(Foo, lambda: [ObjectField("bar", int)])

foo = deserialize(Foo, {"bar": 0})
assert isinstance(foo, Foo) and foo.bar == 0
assert serialize(Foo, Foo(0)) == {"bar": 0}
assert deserialization_schema(Foo) == {
    "$schema": "http://json-schema.org/draft/2019-09/schema#",
    "type": "object",
    "properties": {
        "bar": {
            "type": "integer"
        }
    },
    "required": ["bar"],
    "additionalProperties": False,
}
Esempio n. 9
0
def resolver_resolve(
    resolver: Resolver,
    types: Mapping[str, AnyType],
    aliaser: Aliaser,
    default_deserialization: DefaultConversion,
    default_serialization: DefaultConversion,
    serialized: bool = True,
) -> Callable:
    # graphql deserialization will give Enum objects instead of strings
    def handle_enum(tp: AnyType) -> Optional[AnyConversion]:
        if is_type(tp) and issubclass(tp, Enum):
            return Conversion(identity, source=Any, target=tp)
        return default_deserialization(tp)

    parameters, info_parameter = [], None
    for param in resolver.parameters:
        param_type = types[param.name]
        if is_union_of(param_type, graphql.GraphQLResolveInfo):
            info_parameter = param.name
        else:
            param_field = ObjectField(
                param.name,
                param_type,
                param.default is Parameter.empty,
                resolver.parameters_metadata.get(param.name, empty_dict),
                param.default,
            )
            deserializer = deserialization_method(
                param_type,
                additional_properties=False,
                aliaser=aliaser,
                coerce=False,
                conversion=param_field.deserialization,
                default_conversion=handle_enum,
                fall_back_on_default=False,
                schema=param_field.schema,
            )
            opt_param = is_union_of(param_type, NoneType) or param.default is None
            parameters.append(
                (
                    aliaser(param_field.alias),
                    param.name,
                    deserializer,
                    opt_param,
                    param_field.required,
                )
            )
    func, error_handler = resolver.func, resolver.error_handler
    method_factory = partial_serialization_method_factory(
        aliaser, resolver.conversion, default_serialization
    )

    serialize_result: Callable[[Any], Any]
    if not serialized:
        serialize_result = lambda res: res
    elif is_async(resolver.func):
        serialize_result = as_async(method_factory(types["return"]))
    else:
        serialize_result = method_factory(types["return"])
    serialize_error: Optional[Callable[[Any], Any]]
    if error_handler is None:
        serialize_error = None
    elif is_async(error_handler):
        serialize_error = as_async(method_factory(resolver.error_type()))
    else:
        serialize_error = method_factory(resolver.error_type())

    def resolve(__self, __info, **kwargs):
        values = {}
        errors: Dict[str, ValidationError] = {}
        for alias, param_name, deserializer, opt_param, required in parameters:
            if alias in kwargs:
                # It is possible for the parameter to be non-optional in Python
                # type hints but optional in the generated schema. In this case
                # we should ignore it.
                # See: https://github.com/wyfo/apischema/pull/130#issuecomment-845497392
                if not opt_param and kwargs[alias] is None:
                    assert not required
                    continue
                try:
                    values[param_name] = deserializer(kwargs[alias])
                except ValidationError as err:
                    errors[aliaser(param_name)] = err
            elif opt_param and required:
                values[param_name] = None

        if errors:
            raise ValueError(ValidationError(children=errors).errors)
        if info_parameter:
            values[info_parameter] = __info
        try:
            return serialize_result(func(__self, **values))
        except Exception as error:
            if error_handler is None:
                raise
            assert serialize_error is not None
            return serialize_error(error_handler(error, __self, __info, **kwargs))

    return resolve