async def test_send_result(redis_result_transport: RedisResultTransport,
                           redis_client):
    await redis_result_transport.send_result(
        rpc_message=RpcMessage(
            id="123abc",
            api_name="my.api",
            procedure_name="my_proc",
            kwargs={"field": "value"},
            return_path="abc",
        ),
        result_message=ResultMessage(id="345",
                                     rpc_message_id="123abc",
                                     result="All done! 😎"),
        return_path=
        "redis+key://my.api.my_proc:result:e1821498-e57c-11e7-af9d-7831c1c3936e",
        bus_client=None,
    )
    assert await redis_client.keys("*") == [
        b"my.api.my_proc:result:e1821498-e57c-11e7-af9d-7831c1c3936e"
    ]

    result = await redis_client.lpop(
        "my.api.my_proc:result:e1821498-e57c-11e7-af9d-7831c1c3936e")
    assert json.loads(result) == {
        "metadata": {
            "error": False,
            "rpc_message_id": "123abc",
            "id": "345"
        },
        "kwargs": {
            "result": "All done! 😎"
        },
    }
Esempio n. 2
0
    async def _consume_rpcs_with_transport(self,
                                           rpc_transport: RpcTransport,
                                           apis: List[Api] = None):
        rpc_messages = await rpc_transport.consume_rpcs(apis)
        for rpc_message in rpc_messages:
            self._validate(rpc_message, "incoming")

            await self._plugin_hook("before_rpc_execution",
                                    rpc_message=rpc_message)
            try:
                result = await self.call_rpc_local(
                    api_name=rpc_message.api_name,
                    name=rpc_message.procedure_name,
                    kwargs=rpc_message.kwargs,
                )
            except SuddenDeathException:
                # Used to simulate message failure for testing
                pass
            else:
                result = deform_to_bus(result)
                result_message = ResultMessage(result=result,
                                               rpc_message_id=rpc_message.id)
                await self._plugin_hook("after_rpc_execution",
                                        rpc_message=rpc_message,
                                        result_message=result_message)

                self._validate(
                    result_message,
                    "outgoing",
                    api_name=rpc_message.api_name,
                    procedure_name=rpc_message.procedure_name,
                )

                await self.send_result(rpc_message=rpc_message,
                                       result_message=result_message)
Esempio n. 3
0
async def test_send_result(redis_result_transport: RedisResultTransport,
                           redis_client):
    await redis_result_transport.send_result(
        rpc_message=RpcMessage(
            rpc_id='123abc',
            api_name='my.api',
            procedure_name='my_proc',
            kwargs={'field': 'value'},
            return_path='abc',
        ),
        result_message=ResultMessage(
            rpc_id='123abc',
            result='All done! 😎',
        ),
        return_path=
        'redis+key://my.api.my_proc:result:e1821498-e57c-11e7-af9d-7831c1c3936e',
    )
    assert await redis_client.keys('*') == [
        b'my.api.my_proc:result:e1821498-e57c-11e7-af9d-7831c1c3936e'
    ]

    result = await redis_client.lpop(
        'my.api.my_proc:result:e1821498-e57c-11e7-af9d-7831c1c3936e')
    assert json.loads(result) == {
        'error': False,
        'rpc_id': '123abc',
        'result': 'All done! 😎',
    }
Esempio n. 4
0
    async def receive_result(
        self, rpc_message: RpcMessage, return_path: str, options: dict
    ) -> ResultMessage:
        logger.info("⌛ Faking listening for results. Will issue fake result in 0.5 seconds...")
        await asyncio.sleep(0.1)  # This is relied upon in testing
        logger.debug("Faking received result")

        return ResultMessage(result="Fake result", rpc_message_id=rpc_message.id)
Esempio n. 5
0
    async def call_rpc(self, rpc_message: RpcMessage, options: dict, bus_client: "BusClient"):
        # Direct RPC transport calls API method immediately
        logger.debug("Directly executing RPC call for message {}".format(rpc_message))
        api = registry.get(rpc_message.api_name)
        result = await getattr(api, rpc_message.procedure_name)(**rpc_message.kwargs)

        logger.debug("Sending result for message {}".format(rpc_message))
        await self.result_transport.send_result(
            rpc_message=rpc_message,
            result_message=ResultMessage(result=result, rpc_message_id=rpc_message.id),
            return_path=rpc_message.return_path,
        )
        logger.info(
            "⚡️  Directly executed RPC call & sent result for message {}.".format(rpc_message)
        )
Esempio n. 6
0
    async def receive_result(self, rpc_message: RpcMessage, return_path: str, options: dict) -> ResultMessage:
        logger.info(L("⌛ Awaiting Redis result for RPC message: {}", Bold(rpc_message)))
        redis_key = self._parse_return_path(return_path)

        pool = await self.get_redis_pool()
        with await pool as redis:
            start_time = time.time()
            # TODO: Make timeout configurable
            _, result = await redis.blpop(redis_key, timeout=5)
            result_dictionary = redis_decode(result)

        logger.info(L(
            "⬅ Received Redis result in {} for RPC message {}: {}",
            human_time(time.time() - start_time), rpc_message, Bold(result)
        ))

        return ResultMessage.from_dict(result_dictionary)
Esempio n. 7
0
    async def _consume_rpcs_with_transport(
        self, rpc_transport: RpcTransport, apis: List[Api] = None
    ):
        while True:
            try:
                rpc_messages = await rpc_transport.consume_rpcs(apis, bus_client=self)
            except TransportIsClosed:
                return

            for rpc_message in rpc_messages:
                self._validate(rpc_message, "incoming")

                await self._execute_hook("before_rpc_execution", rpc_message=rpc_message)
                try:
                    result = await self.call_rpc_local(
                        api_name=rpc_message.api_name,
                        name=rpc_message.procedure_name,
                        kwargs=rpc_message.kwargs,
                    )
                except SuddenDeathException:
                    # Used to simulate message failure for testing
                    return
                except CancelledError:
                    raise
                except Exception as e:
                    result = e
                else:
                    result = deform_to_bus(result)

                result_message = ResultMessage(result=result, rpc_message_id=rpc_message.id)
                await self._execute_hook(
                    "after_rpc_execution", rpc_message=rpc_message, result_message=result_message
                )

                self._validate(
                    result_message,
                    "outgoing",
                    api_name=rpc_message.api_name,
                    procedure_name=rpc_message.procedure_name,
                )

                await self.send_result(rpc_message=rpc_message, result_message=result_message)
Esempio n. 8
0
    async def send_result(self, rpc_message: RpcMessage, result_message: ResultMessage, return_path: str):
        logger.debug(L(
            "Sending result {} into Redis using return path {}",
            Bold(result_message), Bold(return_path)
        ))
        redis_key = self._parse_return_path(return_path)

        pool = await self.get_redis_pool()
        with await pool as redis:
            start_time = time.time()
            p = redis.pipeline()
            p.lpush(redis_key, redis_encode(result_message.to_dict()))
            # TODO: Make result expiry configurable
            p.expire(redis_key, timeout=60)
            await p.execute()

        logger.debug(L(
            "➡ Sent result {} into Redis in {} using return path {}",
            Bold(result_message), human_time(time.time() - start_time), Bold(return_path)
        ))
Esempio n. 9
0
    async def consume_rpcs(self, apis=None):
        if apis is None:
            apis = registry.all()

        while True:
            rpc_messages = await self.rpc_transport.consume_rpcs(apis)
            for rpc_message in rpc_messages:
                await plugin_hook('before_rpc_execution', rpc_message=rpc_message, bus_client=self)
                try:
                    result = await self.call_rpc_local(
                        api_name=rpc_message.api_name,
                        name=rpc_message.procedure_name,
                        kwargs=rpc_message.kwargs
                    )
                except SuddenDeathException:
                    # Used to simulate message failure for testing
                    pass
                else:
                    result_message = ResultMessage(result=result, rpc_id=rpc_message.rpc_id)
                    await plugin_hook('after_rpc_execution', rpc_message=rpc_message, result_message=result_message,
                                      bus_client=self)
                    await self.send_result(rpc_message=rpc_message, result_message=result_message)
Esempio n. 10
0
    async def handle_execute_rpc(self, command: commands.ExecuteRpcCommand):
        await self.schema.ensure_loaded_from_bus()
        validate_incoming(self.config, self.schema, command.message)

        await self.hook_registry.execute("before_rpc_execution", rpc_message=command.message)
        try:
            result = await self._call_rpc_local(
                api_name=command.message.api_name,
                name=command.message.procedure_name,
                kwargs=command.message.kwargs,
            )
        except SuddenDeathException:
            # Used to simulate message failure for testing
            return
        except asyncio.CancelledError:
            raise
        except Exception as e:
            result = e
        else:
            result = deform_to_bus(result)

        result_message = ResultMessage(
            result=result,
            rpc_message_id=command.message.id,
            api_name=command.message.api_name,
            procedure_name=command.message.procedure_name,
        )
        await self.hook_registry.execute(
            "after_rpc_execution", rpc_message=command.message, result_message=result_message
        )

        if not result_message.error:
            validate_outgoing(self.config, self.schema, result_message)

        await self.producer.send(
            commands.SendResultCommand(message=result_message, rpc_message=command.message)
        ).wait()