From 8f5e12819ae8bf20d611948b02ae369946fb509a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B8=D1=81=D0=BE=D0=B2=20=D0=9A=D0=B8=D1=80=D0=B8?= =?UTF-8?q?=D0=BB=D0=BB?= Date: Mon, 18 Sep 2023 17:27:05 +0300 Subject: [PATCH] Feat/botx metrics (#407) * feat: add botx metrics * bump: version --- pybotx/bot/bot.py | 33 ++++++++++++ pybotx/client/mertics_api/__init__.py | 0 .../mertics_api/collect_bot_function.py | 43 +++++++++++++++ pyproject.toml | 2 +- tests/client/test_metrics_api/__init__.py | 0 .../test_metrics_api/test_collect_metric.py | 54 +++++++++++++++++++ 6 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 pybotx/client/mertics_api/__init__.py create mode 100644 pybotx/client/mertics_api/collect_bot_function.py create mode 100644 tests/client/test_metrics_api/__init__.py create mode 100644 tests/client/test_metrics_api/test_collect_metric.py diff --git a/pybotx/bot/bot.py b/pybotx/bot/bot.py index 3b71dc8d..47447a6e 100644 --- a/pybotx/bot/bot.py +++ b/pybotx/bot/bot.py @@ -97,6 +97,10 @@ UploadFileMethod, ) from pybotx.client.get_token import get_token +from pybotx.client.mertics_api.collect_bot_function import ( + BotXAPICollectBotFunctionRequestPayload, + CollectBotFunctionMethod, +) from pybotx.client.notifications_api.direct_notification import ( BotXAPIDirectNotificationRequestPayload, DirectNotificationMethod, @@ -1778,6 +1782,35 @@ async def refresh_access_token( ) await method.execute(payload) + # - Metrics API - + async def collect_metric( + self, + bot_id: UUID, + bot_function: str, + huids: List[UUID], + chat_id: UUID, + ) -> None: + """Collect a new use of the bot function. + + :param bot_id: Bot which should perform the request. + :param bot_function: Name of the bot function. + :param huids: Users involved in using the function. + :param chat_id: Chat in which the function was used. + """ + + method = CollectBotFunctionMethod( + bot_id, + self._httpx_client, + self._bot_accounts_storage, + ) + + payload = BotXAPICollectBotFunctionRequestPayload.from_domain( + bot_function=bot_function, + huids=huids, + chat_id=chat_id, + ) + await method.execute(payload) + @staticmethod def _build_main_collector( collectors: Sequence[HandlerCollector], diff --git a/pybotx/client/mertics_api/__init__.py b/pybotx/client/mertics_api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pybotx/client/mertics_api/collect_bot_function.py b/pybotx/client/mertics_api/collect_bot_function.py new file mode 100644 index 00000000..e72a8052 --- /dev/null +++ b/pybotx/client/mertics_api/collect_bot_function.py @@ -0,0 +1,43 @@ +from typing import List, Literal +from uuid import UUID + +from pybotx.client.authorized_botx_method import AuthorizedBotXMethod +from pybotx.models.api_base import UnverifiedPayloadBaseModel, VerifiedPayloadBaseModel + + +class BotXAPICollectBotFunctionRequestPayload(UnverifiedPayloadBaseModel): + group_chat_id: UUID + user_huids: List[UUID] + bot_function: str + + @classmethod + def from_domain( + cls, + chat_id: UUID, + huids: List[UUID], + bot_function: str, + ) -> "BotXAPICollectBotFunctionRequestPayload": + return cls(group_chat_id=chat_id, user_huids=huids, bot_function=bot_function) + + +class BotXAPICollectBotFunctionResponsePayload(VerifiedPayloadBaseModel): + status: Literal["ok"] + result: bool + + +class CollectBotFunctionMethod(AuthorizedBotXMethod): + async def execute( + self, + payload: BotXAPICollectBotFunctionRequestPayload, + ) -> None: + path = "/api/v3/botx/metrics/bot_function" + + response = await self._botx_method_call( + "POST", + self._build_url(path), + json=payload.jsonable_dict(), + ) + self._verify_and_extract_api_model( + BotXAPICollectBotFunctionResponsePayload, + response, + ) diff --git a/pyproject.toml b/pyproject.toml index dc384641..6ca97948 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pybotx" -version = "0.57.0" +version = "0.58.0" description = "A python library for interacting with eXpress BotX API" authors = [ "Sidnev Nikolay ", diff --git a/tests/client/test_metrics_api/__init__.py b/tests/client/test_metrics_api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/client/test_metrics_api/test_collect_metric.py b/tests/client/test_metrics_api/test_collect_metric.py new file mode 100644 index 00000000..aa77b79c --- /dev/null +++ b/tests/client/test_metrics_api/test_collect_metric.py @@ -0,0 +1,54 @@ +from http import HTTPStatus +from uuid import UUID + +import httpx +import pytest +from respx import MockRouter + +from pybotx import Bot, BotAccountWithSecret, HandlerCollector, lifespan_wrapper + +pytestmark = [ + pytest.mark.asyncio, + pytest.mark.mock_authorization, + pytest.mark.usefixtures("respx_mock"), +] + + +async def test__collect_bot_function_metric__success( + respx_mock: MockRouter, + host: str, + bot_id: UUID, + bot_account: BotAccountWithSecret, +) -> None: + # - Arrange - + endpoint = respx_mock.post( + f"https://{host}/api/v3/botx/metrics/bot_function", + headers={"Authorization": "Bearer token", "Content-Type": "application/json"}, + json={ + "user_huids": ["33bd8924-da34-4615-b8ea-f8f7139bf4ef"], + "group_chat_id": "3c11c28e-fbe1-4080-86e8-58e03c217dae", + "bot_function": "email_sent", + }, + ).mock( + return_value=httpx.Response( + HTTPStatus.ACCEPTED, + json={ + "status": "ok", + "result": True, + }, + ), + ) + + built_bot = Bot(collectors=[HandlerCollector()], bot_accounts=[bot_account]) + + # - Act - + async with lifespan_wrapper(built_bot) as bot: + await bot.collect_metric( + bot_id=bot_id, + bot_function="email_sent", + huids=[UUID("33bd8924-da34-4615-b8ea-f8f7139bf4ef")], + chat_id=UUID("3c11c28e-fbe1-4080-86e8-58e03c217dae"), + ) + + # - Assert - + assert endpoint.called