Exemplo n.º 1
0
    def test_either_left_functor_law2(self) -> None:
        """fmap (f . g) x = fmap f (fmap g x)"""
        def f(x):
            return x + 10

        def g(x):
            return x * 10

        self.assertEqual(
            Left(42).map(f).map(g),
            Left(42).map(lambda x: g(f(x))))
Exemplo n.º 2
0
async def call(request: Request, context: Any, method: Method) -> Result:
    try:
        result = await method(*extract_args(request, context),
                              **extract_kwargs(request))
        validate_result(result)
    except JsonRpcError as exc:
        return Left(
            ErrorResult(code=exc.code, message=exc.message, data=exc.data))
    except Exception as exc:  # Other error inside method - Internal error
        logging.exception(exc)
        return Left(InternalErrorResult(str(exc)))
    return result
Exemplo n.º 3
0
def test_to_response_ErrorResult():
    assert (
        to_response(
            Request("ping", [], sentinel.id),
            Left(
                ErrorResult(
                    code=sentinel.code, message=sentinel.message, data=sentinel.data
                )
            ),
        )
    ) == Left(
        ErrorResponse(sentinel.code, sentinel.message, sentinel.data, sentinel.id)
    )
Exemplo n.º 4
0
def test_call_raising_exception():
    def method():
        raise ValueError("foo")

    assert call(Request("ping", [], 1), NOCONTEXT, method) == Left(
        ErrorResult(ERROR_INTERNAL_ERROR, "Internal error", "foo")
    )
Exemplo n.º 5
0
def test_call_raising_jsonrpcerror():
    def method():
        raise JsonRpcError(code=1, message="foo", data=NODATA)

    assert call(Request("ping", [], 1), NOCONTEXT, method) == Left(
        ErrorResult(1, "foo")
    )
Exemplo n.º 6
0
def dispatch_to_response_pure(
    *,
    deserializer: Callable[[str], Deserialized],
    validator: Callable[[Deserialized], Deserialized],
    methods: Methods,
    context: Any,
    post_process: Callable[[Response], Iterable[Any]],
    request: str,
) -> Union[Response, List[Response], None]:
    """A function from JSON-RPC request string to Response namedtuple(s), (yet to be
    serialized to json).

    Returns: A single Response, a list of Responses, or None. None is given for
        notifications or batches of notifications, to indicate that we should not
        respond.
    """
    try:
        result = deserialize_request(deserializer, request).bind(
            partial(validate_request, validator)
        )
        return (
            post_process(result)
            if isinstance(result, Left)
            else dispatch_deserialized(methods, context, post_process, result._value)
        )
    except Exception as exc:
        # There was an error with the jsonrpcserver library.
        logging.exception(exc)
        return post_process(Left(ServerErrorResponse(str(exc), None)))
Exemplo n.º 7
0
def test_validate_result_positionals_not_passed():
    assert validate_args(
        Request("f", {"foo": "bar"}, NOID), NOCONTEXT, lambda x: None
    ) == Left(
        ErrorResult(
            ERROR_INVALID_PARAMS, "Invalid params", "missing a required argument: 'x'"
        )
    )
Exemplo n.º 8
0
def test_validate_result_no_arguments_too_many_positionals():
    assert validate_args(Request("f", ["foo"], NOID), NOCONTEXT, lambda: None) == Left(
        ErrorResult(
            code=ERROR_INVALID_PARAMS,
            message="Invalid params",
            data="too many positional arguments",
        )
    )
Exemplo n.º 9
0
def test_validate_request_invalid():
    assert validate_request(default_validator, {"jsonrpc": "2.0"}) == Left(
        ErrorResponse(
            ERROR_INVALID_REQUEST,
            "Invalid request",
            "The request failed schema validation",
            None,
        )
    )
Exemplo n.º 10
0
def get_method(methods: Methods, method_name: str) -> Either[ErrorResult, Method]:
    """Get the requested method from the methods dict.

    Returns: Either the function to be called, or a Method Not Found result.
    """
    try:
        return Right(methods[method_name])
    except KeyError:
        return Left(MethodNotFoundResult(method_name))
Exemplo n.º 11
0
def test_dispatch_to_response_pure_method_not_found():
    assert dispatch_to_response_pure(
        deserializer=default_deserializer,
        validator=default_validator,
        post_process=identity,
        context=NOCONTEXT,
        methods={},
        request='{"jsonrpc": "2.0", "method": "non_existant", "id": 1}',
    ) == Left(
        ErrorResponse(ERROR_METHOD_NOT_FOUND, "Method not found", "non_existant", 1)
    )
Exemplo n.º 12
0
def validate_args(
    request: Request, context: Any, func: Method
) -> Either[ErrorResult, Method]:
    """Ensure the method can be called with the arguments given.

    Returns: Either the function to be called, or an Invalid Params error result.
    """
    try:
        signature(func).bind(*extract_args(request, context), **extract_kwargs(request))
    except TypeError as exc:
        return Left(InvalidParamsResult(str(exc)))
    return Right(func)
Exemplo n.º 13
0
async def test_dispatch_to_response_pure_server_error(*_):
    async def foo():
        return Success()

    assert (await dispatch_to_response_pure(
        deserializer=default_deserializer,
        validator=default_validator,
        post_process=identity,
        context=NOCONTEXT,
        methods={"foo": foo},
        request='{"jsonrpc": "2.0", "method": "foo", "id": 1}',
    ) == Left(ErrorResponse(ERROR_SERVER_ERROR, "Server error", "foo", None)))
Exemplo n.º 14
0
def call(request: Request, context: Any, method: Method) -> Result:
    """Call the method.

    Handles any exceptions raised in the method, being sure to return an Error response.

    Returns: A Result.
    """
    try:
        result = method(*extract_args(request, context), **extract_kwargs(request))
        # validate_result raises AssertionError if the return value is not a valid
        # Result, which should respond with Internal Error because its a problem in the
        # method.
        validate_result(result)
    # Raising JsonRpcError inside the method is an alternative way of returning an error
    # response.
    except JsonRpcError as exc:
        return Left(ErrorResult(code=exc.code, message=exc.message, data=exc.data))
    # Any other uncaught exception inside method - internal error.
    except Exception as exc:
        logging.exception(exc)
        return Left(InternalErrorResult(str(exc)))
    return result
Exemplo n.º 15
0
def test_to_serializable_ErrorResponse():
    assert to_serializable(
        Left(
            ErrorResponse(sentinel.code, sentinel.message, sentinel.data,
                          sentinel.id))) == {
                              "jsonrpc": "2.0",
                              "error": {
                                  "code": sentinel.code,
                                  "message": sentinel.message,
                                  "data": sentinel.data,
                              },
                              "id": sentinel.id,
                          }
Exemplo n.º 16
0
def deserialize_request(
    deserializer: Callable[[str], Deserialized], request: str
) -> Either[ErrorResponse, Deserialized]:
    """Parse the JSON request string.

    Returns: Either the deserialized request or a "Parse Error" response.
    """
    try:
        return Right(deserializer(request))
    # Since the deserializer is unknown, the specific exception that will be raised is
    # also unknown. Any exception raised we assume the request is invalid, return a
    # parse error response.
    except Exception as exc:
        return Left(ParseErrorResponse(str(exc)))
Exemplo n.º 17
0
def test_dispatch_to_response_pure_internal_error():
    def foo():
        raise ValueError("foo")

    assert (
        dispatch_to_response_pure(
            deserializer=default_deserializer,
            validator=default_validator,
            post_process=identity,
            context=NOCONTEXT,
            methods={"foo": foo},
            request='{"jsonrpc": "2.0", "method": "foo", "id": 1}',
        )
        == Left(ErrorResponse(ERROR_INTERNAL_ERROR, "Internal error", "foo", 1))
    )
Exemplo n.º 18
0
def test_dispatch_to_response_pure_invalid_params_explicitly_returned():
    def foo(colour: str) -> Result:
        if colour not in ("orange", "red", "yellow"):
            return InvalidParams()

    assert (
        dispatch_to_response_pure(
            deserializer=default_deserializer,
            validator=default_validator,
            post_process=identity,
            context=NOCONTEXT,
            methods={"foo": foo},
            request='{"jsonrpc": "2.0", "method": "foo", "params": ["blue"], "id": 1}',
        )
        == Left(ErrorResponse(ERROR_INVALID_PARAMS, "Invalid params", NODATA, 1))
    )
Exemplo n.º 19
0
def validate_request(
    validator: Callable[[Deserialized], Deserialized], request: Deserialized
) -> Either[ErrorResponse, Deserialized]:
    """Validate the request against a JSON-RPC schema.

    Ensures the parsed request is valid JSON-RPC.

    Returns: Either the same request passed in or an Invalid request response.
    """
    try:
        validator(request)
    # Since the validator is unknown, the specific exception that will be raised is also
    # unknown. Any exception raised we assume the request is invalid and  return an
    # "invalid request" response.
    except Exception as exc:
        return Left(InvalidRequestResponse("The request failed schema validation"))
    return Right(request)
Exemplo n.º 20
0
def to_response(request: Request, result: Result) -> Response:
    """Maps a Request plus a Result to a Response. A Response is just a Result plus the
    id from the original Request.

    Raises: AssertionError if the request is a notification. Notifications can't be
        responded to. If a notification is given and AssertionError is raised, we should
        respond with Server Error, because notifications should have been removed by
        this stage.

    Returns: A Response.
    """
    assert request.id is not NOID
    return (
        Left(ErrorResponse(**result._error._asdict(), id=request.id))
        if isinstance(result, Left)
        else Right(SuccessResponse(**result._value._asdict(), id=request.id))
    )
Exemplo n.º 21
0
def test_examples_invalid_json():
    response = dispatch_to_response_pure(
        methods={"ping": ping},
        context=NOCONTEXT,
        validator=default_validator,
        post_process=identity,
        deserializer=default_deserializer,
        request='[{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, {"jsonrpc": "2.0", "method"]',
    )
    assert response == Left(
        ErrorResponse(
            ERROR_PARSE_ERROR,
            "Parse error",
            "Expecting ':' delimiter: line 1 column 96 (char 95)",
            None,
        )
    )
Exemplo n.º 22
0
def test_dispatch_to_response_pure_notification_invalid_request():
    """Invalid JSON-RPC, must return an error. (impossible to determine if notification)"""
    assert dispatch_to_response_pure(
        deserializer=default_deserializer,
        validator=default_validator,
        post_process=identity,
        context=NOCONTEXT,
        methods={"ping": ping},
        request="{}",
    ) == Left(
        ErrorResponse(
            ERROR_INVALID_REQUEST,
            "Invalid request",
            "The request failed schema validation",
            None,
        )
    )
Exemplo n.º 23
0
def test_dispatch_to_response_pure_notification_parse_error():
    """Unable to parse, must return an error"""
    assert dispatch_to_response_pure(
        deserializer=default_deserializer,
        validator=default_validator,
        post_process=identity,
        context=NOCONTEXT,
        methods={"ping": ping},
        request="{",
    ) == Left(
        ErrorResponse(
            ERROR_PARSE_ERROR,
            "Parse error",
            "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
            None,
        )
    )
Exemplo n.º 24
0
def test_dispatch_to_response_pure_raising_exception():
    """Allow raising an exception to return an error."""

    def raise_exception():
        raise JsonRpcError(code=0, message="foo", data="bar")

    assert (
        dispatch_to_response_pure(
            deserializer=default_deserializer,
            validator=default_validator,
            post_process=identity,
            context=NOCONTEXT,
            methods={"raise_exception": raise_exception},
            request='{"jsonrpc": "2.0", "method": "raise_exception", "id": 1}',
        )
        == Left(ErrorResponse(0, "foo", "bar", 1))
    )
Exemplo n.º 25
0
async def dispatch_to_response_pure(
    *,
    deserializer: Callable[[str], Deserialized],
    validator: Callable[[Deserialized], Deserialized],
    methods: Methods,
    context: Any,
    post_process: Callable[[Response], Iterable[Any]],
    request: str,
) -> Union[Response, Iterable[Response], None]:
    try:
        result = deserialize_request(deserializer, request).bind(
            partial(validate_request, validator))
        return (post_process(result)
                if isinstance(result, Left) else await dispatch_deserialized(
                    methods, context, post_process, result._value))
    except Exception as exc:
        logging.exception(exc)
        return post_process(Left(ServerErrorResponse(str(exc), None)))
Exemplo n.º 26
0
    def validate_price_range(ctx: dict) -> Either:
        price_query = price_range

        if not price_query:
            return Right(ctx)

        try:
            price_query = price_query.strip().split(',')
            price_query.sort()
            lower_limit, upper_limit = map(int, price_query)
            if lower_limit < 0 or upper_limit < 0:
                raise ValueError
            price_query = Q(categories__services__price__range=(lower_limit,
                                                                upper_limit))
            return Right(
                dict(condition=ctx.get('condition').add(price_query, Q.AND)))
        except ValueError:
            return Left(dict(error_code=ListDoctors.INVALID_PRICE_RANGE))
Exemplo n.º 27
0
def test_examples_empty_array():
    # This is an invalid JSON-RPC request, should return an error.
    response = dispatch_to_response_pure(
        request="[]",
        methods={"ping": ping},
        context=NOCONTEXT,
        validator=default_validator,
        post_process=identity,
        deserializer=default_deserializer,
    )
    assert response == Left(
        ErrorResponse(
            ERROR_INVALID_REQUEST,
            "Invalid request",
            "The request failed schema validation",
            None,
        )
    )
Exemplo n.º 28
0
def test_dispatch_to_response_pure_invalid_params_auto():
    def foo(colour: str, size: str):
        return Success()

    assert dispatch_to_response_pure(
        deserializer=default_deserializer,
        validator=default_validator,
        post_process=identity,
        context=NOCONTEXT,
        methods={"foo": foo},
        request='{"jsonrpc": "2.0", "method": "foo", "params": {"colour":"blue"}, "id": 1}',
    ) == Left(
        ErrorResponse(
            ERROR_INVALID_PARAMS,
            "Invalid params",
            "missing a required argument: 'size'",
            1,
        )
    )
Exemplo n.º 29
0
def test_examples_multiple_invalid_jsonrpc():
    """
    We break the spec here, by not validating each request in the batch individually.
    The examples are expecting a batch response full of error responses.
    """
    response = dispatch_to_response_pure(
        deserializer=default_deserializer,
        validator=default_validator,
        post_process=identity,
        context=NOCONTEXT,
        methods={"ping": ping},
        request="[1, 2, 3]",
    )
    assert response == Left(
        ErrorResponse(
            ERROR_INVALID_REQUEST,
            "Invalid request",
            "The request failed schema validation",
            None,
        )
    )
Exemplo n.º 30
0
def test_dispatch_to_response_pure_invalid_result():
    """Methods should return a Result, otherwise we get an Internal Error response."""

    def not_a_result():
        return None

    assert dispatch_to_response_pure(
        deserializer=default_deserializer,
        validator=default_validator,
        post_process=identity,
        context=NOCONTEXT,
        methods={"not_a_result": not_a_result},
        request='{"jsonrpc": "2.0", "method": "not_a_result", "id": 1}',
    ) == Left(
        ErrorResponse(
            ERROR_INTERNAL_ERROR,
            "Internal error",
            "The method did not return a valid Result (returned None)",
            1,
        )
    )