Exemplo n.º 1
0
async def test_body_validation():
    @resource
    class Resource:
        @operation
        async def post(self, a: Annotated[int, InBody]) -> str:
            return f"{a}"

    application = Application(Resource())
    request = Request(method="POST", path="/", body=BytesStream(b'{"a": "not_int"}'))
    response = await application(request)
    assert response.status == http.HTTPStatus.BAD_REQUEST.value
Exemplo n.º 2
0
async def test_request_in_body_parameters():
    @resource
    class Resource:
        @operation
        async def post(self, a: Annotated[str, InBody], b: Annotated[str, InBody]) -> str:
            return f"{a}{b}"

    application = Application(Resource())
    request = Request(method="POST", path="/", body=BytesStream(b'{"a": "foo", "b": "bar"}'))
    response = await application(request)
    assert response.status == http.HTTPStatus.OK.value
    assert await body(response) == b"foobar"
Exemplo n.º 3
0
async def test_stream_request_body():
    @resource
    class Resource:
        @operation
        async def post(self, foo: Annotated[Stream, AsBody]) -> BytesStream:
            content = b"".join([b async for b in foo])
            return BytesStream(content)

    application = Application(Resource())
    content = b"abcdefg"
    request = Request(method="POST", path="/", body=BytesStream(content))
    response = await application(request)
    assert response.status == http.HTTPStatus.OK.value
    assert response.headers["Content-Length"] == str(len(content))
    assert await body(response) == content
Exemplo n.º 4
0
async def simple_error_filter(request: Request):
    """Generates a simple JSON error response if an exception is raised."""
    try:
        try:
            yield
        except fondat.error.Error:
            raise
        except Exception as e:
            _logger.exception("unhandled exception")
            raise InternalServerError from e
    except fondat.error.Error as err:
        body = str(err)
        response = Response()
        response.status = err.status
        response.headers["content-type"] = "text/plain; charset=UTF-8"
        response.headers["content-length"] = str(len(body))
        response.body = BytesStream(body.encode())
        yield response
Exemplo n.º 5
0
async def test_request_body_dataclass():
    @dataclass
    class Model:
        a: int
        b: str

    @resource
    class Resource:
        @operation
        async def post(self, val: Annotated[Model, AsBody]) -> Model:
            return val

    application = Application(Resource())
    m = Model(a=1, b="s")
    codec = get_codec(Binary, Model)
    request = Request(method="POST", path="/", body=BytesStream(codec.encode(m)))
    response = await application(request)
    assert response.status == http.HTTPStatus.OK.value
    assert codec.decode(await body(response)) == m
Exemplo n.º 6
0
 async def _handle(self, request: Request) -> Response:
     if not request.path.startswith(self.path):
         raise NotFoundError
     path = request.path[len(self.path):]
     response = Response()
     method = request.method.lower()
     segments = path.split("/") if path else ()
     resource = self.root
     operation = None
     for segment in segments:
         if operation:  # cannot have segments after operation name
             raise NotFoundError
         try:
             resource = await _subordinate(resource, segment)
         except NotFoundError:
             try:
                 operation = getattr(resource, segment)
                 if not fondat.resource.is_operation(operation):
                     raise NotFoundError
             except AttributeError:
                 raise NotFoundError
     if operation:  # operation name as segment (@query or @mutation)
         fondat_op = getattr(operation, "_fondat_operation", None)
         if not fondat_op or not fondat_op.method == method:
             raise MethodNotAllowedError
     else:  # no remaining segments; operation name as HTTP method
         operation = getattr(resource, method, None)
         if not fondat.resource.is_operation(operation):
             raise MethodNotAllowedError
     body = await _decode_body(operation, request)
     params = {}
     signature = inspect.signature(operation)
     hints = typing.get_type_hints(operation, include_extras=True)
     return_hint = hints.get("return", type(None))
     for name, hint in hints.items():
         if name == "return":
             continue
         required = signature.parameters[
             name].default is inspect.Parameter.empty
         param_in = get_param_in(operation, name, hint)
         if isinstance(param_in, AsBody) and body is not None:
             params[name] = body
         elif isinstance(param_in, InBody) and body is not None:
             if param_in.name in body:
                 params[name] = body[param_in.name]
         elif isinstance(param_in, InQuery):
             if param_in.name in request.query:
                 codec = get_codec(String, hint)
                 try:
                     with DecodeError.path_on_error(param_in.name):
                         params[name] = codec.decode(
                             request.query[param_in.name])
                 except DecodeError as de:
                     raise BadRequestError from de
         if name not in params and required:
             if not is_optional(hint):
                 raise BadRequestError from DecodeError(
                     "required parameter", ["«params»", name])
             params[name] = None
     result = await operation(**params)
     if not is_subclass(return_hint, Stream):
         return_codec = get_codec(Binary, return_hint)
         try:
             result = BytesStream(return_codec.encode(result),
                                  return_codec.content_type)
         except Exception as e:
             raise InternalServerError from e
     response.body = result
     response.headers["Content-Type"] = response.body.content_type
     if response.body.content_length is not None:
         if response.body.content_length == 0:
             response.status = http.HTTPStatus.NO_CONTENT.value
         else:
             response.headers["Content-Length"] = str(
                 response.body.content_length)
     return response
Exemplo n.º 7
0
async def test_bytes_stream():
    value = b"hello"
    assert await _ajoin(BytesStream(value)) == value
Exemplo n.º 8
0
 async def post(self, foo: Annotated[Stream, AsBody]) -> BytesStream:
     content = b"".join([b async for b in foo])
     return BytesStream(content)
Exemplo n.º 9
0
 async def get(self) -> Stream:
     return BytesStream(b"12345")