def test_either_right_functor_law2(self) -> None: """fmap (f . g) x = fmap f (fmap g x)""" def f(x: int) -> int: return x + 10 def g(x: int) -> int: return x * 10 self.assertEqual( Right(42).map(f).map(g), Right(42).map(lambda x: g(f(x))))
def test_dispatch_to_response_with_global_methods(): @method def ping(): return Success("pong") response = dispatch_to_response('{"jsonrpc": "2.0", "method": "ping", "id": 1}') assert response == Right(SuccessResponse("pong", 1))
def test_validate_result_object_method(): class FooClass: def foo(self, one, two): return "bar" f = FooClass().foo assert validate_args(Request("f", ["one", "two"], NOID), NOCONTEXT, f) == Right(f)
def test_to_serializable_SuccessResponse(): assert to_serializable(Right(SuccessResponse(sentinel.result, sentinel.id))) == { "jsonrpc": "2.0", "result": sentinel.result, "id": sentinel.id, }
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))
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))
async def test_dispatch_to_response_pure_success(): assert (await dispatch_to_response_pure( deserializer=default_deserializer, validator=default_validator, post_process=identity, context=NOCONTEXT, methods={"ping": ping}, request='{"jsonrpc": "2.0", "method": "ping", "id": 1}', ) == Right(SuccessResponse("pong", 1)))
def test_dispatch_deserialized(): assert ( dispatch_deserialized( methods={"ping": ping}, context=NOCONTEXT, post_process=identity, deserialized={"jsonrpc": "2.0", "method": "ping", "id": 1}, ) == Right(SuccessResponse("pong", 1)) )
async def test_dispatch_deserialized(): assert (await dispatch_deserialized( {"ping": ping}, NOCONTEXT, identity, { "jsonrpc": "2.0", "method": "ping", "id": 1 }, ) == Right(SuccessResponse("pong", 1)))
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)
def generate_filter(ctx: dict) -> Right: condition = ctx.get('condition') if district: condition.add(Q(district=district.strip().lower()), Q.AND) if category: condition.add(Q(categories__query_name=category.strip().lower()), Q.AND) if language: condition.add(Q(languages__query_name=language.strip().lower()), Q.AND) return Right(dict(condition=condition))
def get_doctor_by_id(doctor_id: str) -> dict: def get_doctor_object(ctx: dict) -> Either: doctor = Doctor.objects.filter(pk=ctx.get('doctor_id')).first() if not doctor: return Left(dict(ctx, error_code=GetDoctor.DOCTOR_NOT_FOUND)) return Right(dict(doctor=DoctorSerializer(doctor).data)) try: result: Either = Right(dict(doctor_id=doctor_id)) | get_doctor_object return result.value except Exception: return dict(error_code=GetDoctor.INTERNAL_SERVER_ERROR)
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)))
def list_doctors(district: Union[str, None] = None, category: Union[str, None] = None, language: Union[str, None] = None, price_range: Union[str, None] = None) -> dict: 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)) def generate_filter(ctx: dict) -> Right: condition = ctx.get('condition') if district: condition.add(Q(district=district.strip().lower()), Q.AND) if category: condition.add(Q(categories__query_name=category.strip().lower()), Q.AND) if language: condition.add(Q(languages__query_name=language.strip().lower()), Q.AND) return Right(dict(condition=condition)) def filter_doctors(ctx: dict) -> Right: doctors = Doctor.objects.filter(ctx.get('condition')).distinct() if not doctors: return Right(dict(doctors=list())) return Right(dict(doctors=DoctorSerializer(doctors, many=True).data)) try: result: Either = Right(dict(condition=Q())) | validate_price_range |\ generate_filter | filter_doctors return result.value except Exception: return dict(error_code=ListDoctors.INTERNAL_SERVER_ERROR)
def test_examples_positionals(): def subtract(minuend, subtrahend): return Success(minuend - subtrahend) response = dispatch_to_response_pure( methods={"subtract": subtract}, context=NOCONTEXT, validator=default_validator, post_process=identity, deserializer=default_deserializer, request='{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}', ) assert response == Right(SuccessResponse(19, 1)) # Second example response = dispatch_to_response_pure( methods={"subtract": subtract}, context=NOCONTEXT, validator=default_validator, post_process=identity, deserializer=default_deserializer, request='{"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}', ) assert response == Right(SuccessResponse(-19, 2))
def test_examples_nameds(): def subtract(**kwargs): return Success(kwargs["minuend"] - kwargs["subtrahend"]) response = dispatch_to_response_pure( methods={"subtract": subtract}, context=NOCONTEXT, validator=default_validator, post_process=identity, deserializer=default_deserializer, request='{"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}', ) assert response == Right(SuccessResponse(19, 3)) # Second example response = dispatch_to_response_pure( methods={"subtract": subtract}, context=NOCONTEXT, validator=default_validator, post_process=identity, deserializer=default_deserializer, request='{"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}', ) assert response == Right(SuccessResponse(19, 4))
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)
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)) )
def test_either_right_map(self) -> None: a = Right(42).map(lambda x: x * 10) self.assertEqual(a, Right(420))
def test_right_applicative_3(self) -> None: a = Right.pure(lambda x, y: x + y).apply(Right(42)).apply( Left("error")) self.assertEqual(a, Left("error"))
def test_right_applicative_1(self) -> None: a = Right.pure(lambda x, y: x + y).apply(Right(2)).apply(Right(40)) self.assertNotEqual(a, Left(42)) self.assertEqual(a, Right(42))
def test_right_applicative_3(self): a = Right.pure(lambda x, y: x+y).apply(Right(42)).apply(Left("error")) self.assertEquals(a, Left("error"))
async def test_call(): assert await call(Request("ping", [], 1), NOCONTEXT, ping) == Right(SuccessResult("pong"))
def Success(*args: Any, **kwargs: Any) -> Either[ErrorResult, SuccessResult]: return Right(SuccessResult(*args, **kwargs))
def test_either_monad_right_bind_right(self) -> None: m = Right(42).bind(lambda x: Right(x * 10)) self.assertEqual(m, Right(420))
async def test_dispatch_to_response(): assert await dispatch_to_response( '{"jsonrpc": "2.0", "method": "ping", "id": 1}', {"ping": ping} ) == Right(SuccessResponse("pong", 1))
def test_either_monad_right_bind_left(self) -> None: """Nothing >>= \\x -> return (x*10)""" m = Left("error").bind(lambda x: Right(x * 10)) self.assertEqual(m, Left("error"))
def test_Success(): assert Success() == Right(SuccessResult(None))
async def test_dispatch_request(): request = Request("ping", [], 1) assert await dispatch_request({"ping": ping}, NOCONTEXT, request) == ( request, Right(SuccessResult("pong")), )
def test_either_right_functor_law1(self) -> None: """fmap id = id""" self.assertEqual(Right(3).map(lambda x: x), Right(3))
def filter_doctors(ctx: dict) -> Right: doctors = Doctor.objects.filter(ctx.get('condition')).distinct() if not doctors: return Right(dict(doctors=list())) return Right(dict(doctors=DoctorSerializer(doctors, many=True).data))
def test_right_applicative_1(self): a = Right.pure(lambda x, y: x+y).apply(Right(2)).apply(Right(40)) self.assertNotEquals(a, Left(42)) self.assertEquals(a, Right(42))