Example #1
0
def _add_missing_time(field, data, time_):
    ds = dict2schema({field: fields.Date()})()
    dts = dict2schema({field: NaiveDateTime()})()
    try:
        # let's see if we can load this as a DateTime
        dts.load({field: data[field]})
    except ValidationError:
        # if not, we assume it's a valid date and append the time
        date = ds.load({field: data[field]})[field]
        data[field] = dts.dump({field: datetime.combine(date, time_)})[field]
Example #2
0
def use_rh_args(schema_cls, **kwargs):
    """Similar to ``use_args`` but populates the context from RH attributes.

    The Schema needs a Meta class with an ``rh_context`` attribute specifying
    which attributes should be taken from the current RH.

    :param schema_cls: A marshmallow Schema or an argmap dict.
    :param rh_context: When using an argmap, this argument is required and behaves
                       exactly like the ``rh_context`` Meta attribute mentioned above.
    :param kwargs: Any keyword arguments that are supported by ``use_args`` or the
                   Schema constructor.
    """

    schema_kwargs, default_context, webargs_kwargs = _split_kwargs(kwargs)

    if isinstance(schema_cls, Mapping):
        schema_cls = dict2schema(schema_cls, parser.schema_class)
        rh_context_attrs = schema_kwargs.pop('rh_context')
    elif isinstance(schema_cls, Schema):
        raise TypeError('Pass a schema or an argmap instead of a schema instance to use_rh_args/use_rh_kwargs')
    else:
        if 'rh_context' in schema_kwargs:
            raise TypeError('The `rh_context` kwarg is only supported when passing an argmap')
        rh_context_attrs = schema_cls.Meta.rh_context

    def factory(req):
        context = dict(default_context)
        context.update((arg, getattr(g.rh, arg, None)) for arg in rh_context_attrs)
        return schema_cls(context=context, **schema_kwargs)

    return parser.use_args(factory, **webargs_kwargs)
Example #3
0
def use_args(schema_cls, **kwargs):
    """Similar to webargs' ``use_args`` but allows passing schema kwargs.

    This makes it much easier to use ``partial=True`` for PATCH endpoints.

    :param schema_cls: A marshmallow Schema or an argmap dict.
    :param kwargs: Any keyword arguments that are supported by ``use_args`` or the
                   Schema constructor.
    """
    schema_kwargs, __, webargs_kwargs = _split_kwargs(kwargs)

    if isinstance(schema_cls, Mapping):
        schema_cls = dict2schema(schema_cls, parser.schema_class)
    elif isinstance(schema_cls, Schema):
        raise TypeError('Pass a schema or an argmap instead of a schema instance to use_args/use_kwargs')

    def factory(req):
        return schema_cls(**schema_kwargs)

    return parser.use_args(factory, **webargs_kwargs)
Example #4
0
def test_load_json_returns_missing_if_no_data(mimetype):
    req = mock.Mock()
    req.mimetype = mimetype
    req.get_data.return_value = ""
    schema = dict2schema({"foo": fields.Field()})()
    assert parser.load_json(req, schema) is missing
Example #5
0
def make_route(input_args: dict = None, output_args: dict = None,
               locations: tuple = None, validate: object = None,
               path: str = None, auth_required: callable = None,
               method_types: int = None):
    """
    Functions makes the route for
    wrapped function.
    :param input_args: Map of incoming data scheme
    :param output_args: Map of output scheme
    :param locations: Location of incoming data ('form', 'data', 'json')
    :param validate: Validator of full scheme
    :param path: Stringify path of decorated async function
    :param auth_required: This functions checks credential information inside request headers
    :param method_types: This flags indicates about methods for specific endpoint
    :return: callable
    """
    if not isinstance(method_types, int):
        method_types = GET | POST

    # Prepare dumper for outgoing data
    # if not (isinstance(output_args, dict) or isinstance(output_args, list)):
    #    output_args = {'result': fields.List(fields.Str(), missing=[])}
    if isinstance(output_args, dict):
        schema = dict2schema(output_args)()
    else:
        schema = output_args()

    # Checking locations of data
    # for incoming request
    if not isinstance(locations, tuple):
        locations = ('json', 'querystring')

    def wrapped(fn_: callable):
        # Create the wrapper for original
        # async coroutine function

        @wraps(fn_)
        async def coro_wrapper(request: web.Request):
            # First checking authorization
            auth_data = None
            if inspect.iscoroutinefunction(auth_required):
                auth_data = await auth_required(request)

            # Parsing incoming data using parser and
            # scheme of input arguments
            parsed_data_map = dict()
            if input_args:
                parsed_data_map = await aiohttpparser.parser.parse(
                    input_args, request, locations, validate
                )

            # Injecting data from request's header
            # data
            if auth_data:
                parsed_data_map = {**parsed_data_map, **auth_data}

            # Waiting the result from original function
            # and dumping it
            data = await fn_(**{**parsed_data_map, **{'request': request}})

            if isinstance(data, (web.FileResponse, web.WebSocketResponse, web.Response)):
                return data

            # 1) Validating outgoing data scheme
            # 2) Serialize verified data
            if data is None:
                data = {}
            else:
                if isinstance(output_args, dict):
                    if not isinstance(data, tuple):
                        data = (data,)

                    data = dict(zip(output_args.keys(), data))

            return JSON_RESPONSE(
                schema.dump(data)
            )

        # Last thing is dispatch the route of
        # aiohttp coroutine and it endpoint
        endpoint_path = path
        if not isinstance(endpoint_path, str):
            # First step is extract
            # name of called module
            frm = inspect.stack()[1]
            mod = inspect.getmodule(frm[0])

            # This module name should
            # exclude from global path
            root_file = __name__

            # The full path to wrapped
            # resource endpoint
            resource_path = mod.__name__
            resource_path = resource_path.replace(root_file, '')
            resource_path = resource_path.replace('.', '/')

            # Extract endpoint name
            method_name = __to_camel(fn_.__name__)
            endpoint_path = "/{0}.{1}".format(resource_path, method_name)

        # Add coroutine wrapper inside the webapp
        if (method_types & GET) == GET:
            ROUTE_TABLE.append(
                web.get(endpoint_path, coro_wrapper, allow_head=False)
            )
        if (method_types & POST) == POST:
            ROUTE_TABLE.append(
                web.post(endpoint_path, coro_wrapper)
            )

        if fn_.__doc__:
            coro_wrapper = docs(
                tags=[resource_path.split('/')[-1]], summary=fn_.__doc__
            )(coro_wrapper)

            if schema:
                coro_wrapper = response_schema(schema)(coro_wrapper)
            if input_args:
                coro_wrapper = request_schema(input_args)(coro_wrapper)

        return coro_wrapper

    return wrapped