def __call__(self, **kwargs):
        try:
            if self._type().id_name() in kwargs:
                return self._registry(self._type()).find(
                    kwargs[self._type().id_name()]).to_dict(
                        force_all=self._kernel.is_admin(self._context))
        except KeyError:
            raise ffd.MissingArgument(self._type().id_name())

        limit = None
        offset = None
        if 'limit' in kwargs and 'offset' in kwargs:
            limit = int(kwargs.get('limit'))
            offset = int(kwargs.get('offset'))

        entities = self._registry(self._type())

        if 'criteria' in kwargs:
            criteria = kwargs.get('criteria')
            if isinstance(criteria, str):
                criteria = self._serializer.deserialize(criteria)
            criteria = ffd.BinaryOp.from_dict(criteria)
            if '__include_deleted' not in kwargs and hasattr(
                    self._type(), 'deleted_on'):
                criteria &= ffd.Attr('deleted_on').is_none()
            entities = self._registry(self._type()).filter(criteria)
        elif '__include_deleted' not in kwargs and hasattr(
                self._type(), 'deleted_on'):
            criteria = ffd.Attr('deleted_on').is_none()
            entities = self._registry(self._type()).filter(criteria)

        paginated = False
        count = None
        if limit is not None and offset is not None:
            count = len(entities)
            entities = entities[offset:(offset + limit - 1)]
            paginated = True

        if 'sort' in kwargs:
            entities = entities.sort(lambda x: kwargs.get('sort'))

        if paginated:
            return {
                'offset':
                offset,
                'limit':
                limit,
                'count':
                count,
                'data':
                list(
                    map(
                        lambda e: e.to_dict(force_all=self._kernel.is_admin(
                            self._context)), entities)),
            }

        return list(
            map(
                lambda e: e.to_dict(force_all=self._kernel.is_admin(
                    self._context)), entities))
Beispiel #2
0
 def __call__(self, **kwargs):
     try:
         if 'criteria' in kwargs:
             return self._registry(self._type()).filter(
                 ffd.BinaryOp.from_dict(kwargs['criteria']))
         else:
             return self._registry(self._type())
     except KeyError:
         raise ffd.MissingArgument(self._type().id_name())
    def __call__(self, **kwargs):
        try:
            aggregate = self._registry(self._type()).find(
                kwargs[self._aggregate_name_snake()])
        except KeyError:
            raise ffd.MissingArgument(self._aggregate_name_snake())

        method = getattr(aggregate, self._method)
        return self._buffer_events(
            method(**ffd.build_argument_list(kwargs, method)))
def _build_value_object(obj, type_, required):
    try:
        if isinstance(obj, type_):
            return obj
    except TypeError:
        pass

    try:
        e = _generate_model(obj, type_)
        if e is False and required is True:
            raise ffd.MissingArgument()
        return e
    except ffd.MissingArgument:
        if required is False:
            return
        raise
def _generate_model(args: dict, model_type: type, strict: bool = False):
    subclasses = model_type.__subclasses__()
    if len(subclasses):
        for subclass in subclasses:
            try:
                return _generate_model(args, subclass, strict=True)
            except (RuntimeError, ffd.MissingArgument):
                continue

    entity_args = build_argument_list(args, model_type, strict=False)
    fields_ = {f.name: f for f in fields(model_type)}
    if strict:
        for k in args.keys():
            if k not in entity_args and k in fields_ and fields_[
                    k].metadata.get('required', True) is True:
                raise RuntimeError()

    if len(entity_args.keys()) == 0:
        raise ffd.MissingArgument()

    return model_type(**entity_args)
def _handle_type_hint(params: typing.Any,
                      t: type,
                      key: str = None,
                      required: bool = False):
    # logging.debug('Processing type hint %s with params: %s, key: %s, required: %s', t, params, key, required)
    ret = {}

    origin = ffd.get_origin(t)
    args = ffd.get_args(t)

    if origin is typing.List:
        if key not in params:
            if required:
                raise ffd.MissingArgument(
                    f'Missing argument {key} for type {t}')
            return []

        if ffd.is_type_hint(args[0]):
            if key is not None:
                ret[key] = list(
                    map(lambda a: _handle_type_hint(a, args[0]), params[key]))
            else:
                ret = list(
                    map(lambda a: _handle_type_hint(a, args[0]), params[key]))
        elif inspect.isclass(args[0]) and issubclass(args[0], ffd.ValueObject):
            try:
                if key is not None:
                    ret[key] = []
                    for v in params[key]:
                        parameter = _build_value_object(v, args[0], required)
                        if parameter is not None:
                            ret[key].append(parameter)
                    if len(ret[key]) == 0:
                        del ret[key]
                else:
                    ret = list(
                        map(
                            lambda a: _build_value_object(
                                a, args[0], required), params[key]))
            except TypeError:
                return
        else:
            ret[key] = params[key]

    elif origin is typing.Dict:
        if key not in params:
            if required:
                raise ffd.MissingArgument()
            return {}

        if ffd.is_type_hint(args[1]):
            ret[key] = {
                k: _handle_type_hint(v, args[1])
                for k, v in params[key].items()
            }
        elif inspect.isclass(args[1]) and issubclass(args[1], ffd.ValueObject):
            ret[key] = {
                k: _build_value_object(v, args[1], required)
                for k, v in params[key].items()
            }
        else:
            ret[key] = params[key]

    elif origin is typing.Union:
        for arg in args:
            # logging.debug('Calling _handle_type_hint')
            r = _handle_type_hint(params, arg, key, required)
            # logging.debug('Response: %s', r)
            if r is not void:
                if key is not None:
                    ret[key] = r
                else:
                    ret = r
                break

    else:
        if inspect.isclass(t) and issubclass(t, ffd.ValueObject):
            if key in params:
                return _build_value_object(params[key], t, required)
            else:
                return _build_value_object(params, t, required)

        try:
            if key is not None:
                if key in params:
                    val = _check_special_types(params[key], t)
                    if val is not None:
                        return val

                    try:
                        if t(params[key]) == params[key]:
                            return params[key]
                    except ValueError:
                        pass
                    if params[key] is None:
                        return None
                elif str(t) == "<class 'NoneType'>":
                    return None

            elif key is None and t(params) == params:
                return params
        except (TypeError, KeyError):
            pass

    if ret == {}:
        return void

    return ret
def build_argument_list(params: dict,
                        obj: typing.Union[typing.Callable, type],
                        strict: bool = True,
                        include_none_parameters: bool = False):
    # logging.debug('Building argument list for %s with params: %s, strict: %s', obj, params, strict)
    args = {}
    field_dict = {}
    is_dc = False
    params = _fix_keywords(params)

    if is_dataclass(obj):
        is_dc = True
        field_dict = {}
        # noinspection PyDataclass
        for field_ in fields(obj):
            field_dict[field_.name] = field_
        sig = inspect.signature(obj.__init__)
        types = typing.get_type_hints(obj)
    elif isinstance(obj, ffd.MetaAware):
        sig = inspect.signature(obj.__call__)
        types = typing.get_type_hints(obj.__call__)
    elif isinstance(obj, type):
        sig = inspect.signature(obj.__init__)
        types = typing.get_type_hints(obj.__init__)
    else:
        sig = inspect.signature(obj)
        try:
            types = typing.get_type_hints(obj)
        except NameError:
            types = obj.__annotations__

    has_kwargs = False
    for param in sig.parameters.values():
        if param.kind == inspect.Parameter.VAR_KEYWORD:
            has_kwargs = True
            # return params

    for name, param in sig.parameters.items():
        # logging.debug('Processing param %s', param)
        if name == 'self' or param.kind in (inspect.Parameter.VAR_POSITIONAL,
                                            inspect.Parameter.VAR_KEYWORD):
            # logging.debug('Skipping...')
            continue

        required = False
        if is_dc:
            required = field_dict[name].metadata.get('required', False) is True
            try:
                d = field_dict[name].default_factory()
                if not isinstance(d, ffd.Empty):
                    required = False
            except (AttributeError, TypeError):
                pass
        elif param.default == inspect.Parameter.empty:
            required = True

        type_ = types[name] if name in types else None
        if params and name in params:
            val = _check_special_types(params[name], type_)
            if val is not None:
                params[name] = val

        if isinstance(type_, type) and issubclass(type_, ffd.ValueObject):
            if params is None:
                if required is False:
                    continue
                raise ffd.MissingArgument(name)

            if name in params and isinstance(params[name], type_):
                args[name] = params[name]
                continue

            try:
                nested = False
                e = None
                if name in params and isinstance(params[name], dict):
                    e = _generate_model(params[name], type_)
                    nested = True
                elif isinstance(params, dict):
                    e = _generate_model(copy_params(params, sig), type_)
            except ffd.MissingArgument:
                if required is False:
                    continue
                raise
            # TODO use factories where appropriate
            args[name] = e
            if nested:
                del params[name]

        elif ffd.is_type_hint(type_):
            if type_ is typing.Any:
                if name in params:
                    args[name] = params[name]
            else:
                if name in params:
                    parameter_args = _handle_type_hint({name: params[name]},
                                                       type_,
                                                       key=name,
                                                       required=required)
                elif isinstance(params, dict):
                    parameter_args = _handle_type_hint(copy_params(
                        params, sig),
                                                       type_,
                                                       key=name,
                                                       required=required)
                if parameter_args and not isinstance(parameter_args, Void):
                    args.update(parameter_args)

        elif isinstance(params, dict) and name in params:
            try:
                if params[name] is None:
                    if not is_dc or include_none_parameters is True:
                        args[name] = None
                elif isinstance(params[name], bytes):
                    args[name] = params[name]
                else:
                    try:
                        args[name] = type_(params[name])
                    except OverflowError:
                        args[name] = params[name]
            except TypeError:
                args[name] = params[name]
        elif name.endswith('_') and name.rstrip('_') in params:
            # args[name] = params[name.rstrip('_')]
            try:
                sname = name.rstrip('_')
                if params[sname] is None:
                    if not is_dc or include_none_parameters is True:
                        args[name] = None
                elif isinstance(params[sname], bytes):
                    args[name] = params[sname]
                else:
                    try:
                        args[name] = type_(params[sname])
                    except OverflowError:
                        args[name] = params[name]
            except TypeError:
                args[name] = params[name.rstrip('_')]
        elif required is True and strict:
            raise ffd.MissingArgument(
                f'Argument: {name} is required for object {obj}')

    if has_kwargs:
        for k, v in params.items():
            if k not in args:
                args[k] = v

    # logging.debug('Returning %s', args)
    return args
def build_argument_list(params: dict, obj: typing.Union[typing.Callable, type]):
    args = {}
    field_dict = {}
    is_dc = False
    params = _fix_keywords(params)

    if is_dataclass(obj):
        is_dc = True
        field_dict = {}
        # noinspection PyDataclass
        for field_ in fields(obj):
            field_dict[field_.name] = field_
        sig = inspect.signature(obj.__init__)
        types = typing.get_type_hints(obj)
    elif isinstance(obj, ffd.MetaAware):
        sig = inspect.signature(obj.__call__)
        types = typing.get_type_hints(obj.__call__)
    elif isinstance(obj, type):
        sig = inspect.signature(obj.__init__)
        types = typing.get_type_hints(obj.__init__)
    else:
        sig = inspect.signature(obj)
        try:
            types = typing.get_type_hints(obj)
        except NameError:
            types = obj.__annotations__

    for param in sig.parameters.values():
        if param.kind == inspect.Parameter.VAR_KEYWORD:
            return params

    for name, param in sig.parameters.items():
        if name == 'self' or param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
            continue

        required = False
        if is_dc:
            required = field_dict[name].metadata.get('required', False) is True
            try:
                d = field_dict[name].default_factory()
                if not isinstance(d, ffd.Empty):
                    required = False
            except (AttributeError, TypeError):
                pass
        elif param.default is not None:
            required = True

        type_ = types[name] if name in types else None
        if type_ is datetime and name in params and isinstance(params[name], str):
            params[name] = parse(params[name]).replace(tzinfo=None)

        if isinstance(type_, type) and issubclass(type_, ffd.ValueObject):
            if name in params and isinstance(params[name], type_):
                args[name] = params[name]
                continue

            try:
                nested = False
                if name in params and isinstance(params[name], dict):
                    entity_args = build_argument_list(params[name], type_)
                    nested = True
                else:
                    entity_args = build_argument_list(params, type_)
            except ffd.MissingArgument:
                if required is False:
                    continue
                raise
            # TODO use factories where appropriate
            args[name] = type_(**entity_args)
            if nested:
                del params[name]
            else:
                for key in entity_args.keys():
                    if not hasattr(type_, 'id_name') or key != type_.id_name():
                        del params[key]
                        if key in args:
                            del args[key]
        elif isinstance(type_, type(typing.List)) and issubclass(type_.__args__[0], ffd.ValueObject):
            args[name] = []
            if isinstance(params, dict) and name in params:
                for d in params[name]:
                    try:
                        entity_args = build_argument_list(d, type_.__args__[0])
                    except ffd.MissingArgument:
                        if required is False:
                            continue
                        raise
                    args[name].append(type_.__args__[0](**entity_args))
        elif isinstance(type_, type(typing.Dict)) and len(type_.__args__) == 2 and \
                issubclass(type_.__args__[1], ffd.ValueObject):
            args[name] = {}
            if isinstance(params, dict) and name in params:
                for k, d in params[name].items():
                    try:
                        entity_args = build_argument_list(d, type_.__args__[1])
                    except ffd.MissingArgument:
                        if required is False:
                            continue
                        raise
                    args[name][k] = type_.__args__[1](**entity_args)
        elif isinstance(params, dict) and name in params:
            args[name] = params[name]
        elif name.endswith('_') and name.rstrip('_') in params:
            args[name] = params[name.rstrip('_')]
        elif required is True:
            raise ffd.MissingArgument(f'Argument: {name} is required')

    return args