Skip to content

Commit

Permalink
Change the specification of synchronous requests (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexhook authored Jul 10, 2024
1 parent c6d3d17 commit bf2c157
Show file tree
Hide file tree
Showing 15 changed files with 307 additions and 212 deletions.
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,11 @@ collector = HandlerCollector()
@collector.sync_smartapp_event
async def handle_sync_smartapp_event(
event: SmartAppEvent, bot: Bot,
) -> SyncSmartAppEventResponsePayload:
) -> BotAPISyncSmartAppEventResultResponse:
print(f"Got sync smartapp event: {event}")
return SyncSmartAppEventResponsePayload.from_domain(
ref=event.ref,
smartapp_id=event.bot.id,
chat_id=event.chat.id,
return BotAPISyncSmartAppEventResultResponse.from_domain(
data={},
opts={},
files=[],
encrypted=True,
)
```

Expand Down
12 changes: 8 additions & 4 deletions pybotx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,6 @@
SmartappManifest,
SmartappManifestWebParams,
)
from pybotx.client.smartapps_api.sync_smartapp_event import (
SyncSmartAppEventResponsePayload,
)
from pybotx.client.stickers_api.exceptions import (
InvalidEmojiError,
InvalidImageError,
Expand Down Expand Up @@ -126,6 +123,11 @@
from pybotx.models.smartapps import SmartApp
from pybotx.models.status import BotMenu, StatusRecipient
from pybotx.models.stickers import Sticker, StickerPack
from pybotx.models.sync_smartapp_event import (
BotAPISyncSmartAppEventErrorResponse,
BotAPISyncSmartAppEventResponse,
BotAPISyncSmartAppEventResultResponse,
)
from pybotx.models.system_events.added_to_chat import AddedToChatEvent
from pybotx.models.system_events.chat_created import ChatCreatedEvent, ChatCreatedMember
from pybotx.models.system_events.chat_deleted_by_user import ChatDeletedByUserEvent
Expand All @@ -152,6 +154,9 @@
"BotAPIBotDisabledErrorData",
"BotAPIBotDisabledResponse",
"BotAPIMethodFailedCallback",
"BotAPISyncSmartAppEventErrorResponse",
"BotAPISyncSmartAppEventResponse",
"BotAPISyncSmartAppEventResultResponse",
"BotAPIUnverifiedRequestErrorData",
"BotAPIUnverifiedRequestResponse",
"BotAccount",
Expand Down Expand Up @@ -228,7 +233,6 @@
"RequestHeadersNotProvidedError",
"SmartApp",
"SmartAppEvent",
"SyncSmartAppEventResponsePayload",
"SmartappManifest",
"SmartappManifestWebLayoutChoices",
"SmartappManifestWebParams",
Expand Down
47 changes: 20 additions & 27 deletions pybotx/bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,6 @@
BotXAPISmartAppsListRequestPayload,
SmartAppsListMethod,
)
from pybotx.client.smartapps_api.sync_smartapp_event import (
SyncSmartAppEventResponsePayload,
)
from pybotx.client.smartapps_api.upload_file import (
UploadFileMethod as SmartappsUploadFileMethod,
)
Expand Down Expand Up @@ -221,7 +218,7 @@
ensure_file_content_is_png,
ensure_sticker_image_size_valid,
)
from pybotx.logger import logger, pformat_jsonable_obj, trim_file_data_in_incoming_json
from pybotx.logger import log_incoming_request, logger, pformat_jsonable_obj
from pybotx.missing import Missing, MissingOptional, Undefined
from pybotx.models.async_files import File
from pybotx.models.attachments import IncomingFileAttachment, OutgoingAttachment
Expand All @@ -245,10 +242,11 @@
build_bot_status_response,
)
from pybotx.models.stickers import Sticker, StickerPack, StickerPackFromList
from pybotx.models.system_events.smartapp_event import (
BotAPISmartAppEvent,
SmartAppEvent,
from pybotx.models.sync_smartapp_event import (
BotAPISyncSmartAppEvent,
BotAPISyncSmartAppEventResponse,
)
from pybotx.models.system_events.smartapp_event import SmartAppEvent
from pybotx.models.users import UserFromCSV, UserFromSearch

MissingOptionalAttachment = MissingOptional[
Expand Down Expand Up @@ -299,16 +297,9 @@ def async_execute_raw_bot_command(
logging_command: bool = True,
) -> None:
if logging_command:
logger.opt(lazy=True).debug(
"Got command: {command}",
command=lambda: pformat_jsonable_obj(
trim_file_data_in_incoming_json(raw_bot_command),
),
)
log_incoming_request(raw_bot_command, message="Got command: ")

if verify_request:
if request_headers is None:
raise RequestHeadersNotProvidedError
self._verify_request(request_headers)

try:
Expand Down Expand Up @@ -338,23 +329,19 @@ async def sync_execute_raw_smartapp_event(
verify_request: bool = True,
request_headers: Optional[Mapping[str, str]] = None,
logging_command: bool = True,
) -> SyncSmartAppEventResponsePayload:
) -> BotAPISyncSmartAppEventResponse:
if logging_command:
logger.opt(lazy=True).debug(
"Got sync smartapp event: {command}",
command=lambda: pformat_jsonable_obj(
trim_file_data_in_incoming_json(raw_smartapp_event),
),
log_incoming_request(
raw_smartapp_event,
message="Got sync smartapp event: ",
)

if verify_request:
if request_headers is None:
raise RequestHeadersNotProvidedError
self._verify_request(request_headers)

try:
bot_api_smartapp_event: BotAPISmartAppEvent = parse_obj_as(
BotAPISmartAppEvent,
bot_api_smartapp_event: BotAPISyncSmartAppEvent = parse_obj_as(
BotAPISyncSmartAppEvent,
raw_smartapp_event,
)
except ValidationError as validation_exc:
Expand All @@ -368,7 +355,7 @@ async def sync_execute_raw_smartapp_event(
async def sync_execute_smartapp_event(
self,
smartapp_event: SmartAppEvent,
) -> SyncSmartAppEventResponsePayload:
) -> BotAPISyncSmartAppEventResponse:
self._bot_accounts_storage.ensure_bot_id_exists(smartapp_event.bot.id)
return await self._handler_collector.handle_sync_smartapp_event(
self,
Expand Down Expand Up @@ -2028,7 +2015,13 @@ async def collect_metric(
)
await method.execute(payload)

def _verify_request(self, headers: Mapping[str, str]) -> None: # noqa: WPS238
def _verify_request( # noqa: WPS231, WPS238
self,
headers: Optional[Mapping[str, str]],
) -> None:
if headers is None:
raise RequestHeadersNotProvidedError

authorization_header = headers.get("authorization")
if not authorization_header:
raise UnverifiedRequestError("The authorization token was not provided.")
Expand Down
6 changes: 2 additions & 4 deletions pybotx/bot/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
from functools import partial
from typing import TYPE_CHECKING, Awaitable, Callable, List, Literal, TypeVar, Union

from pybotx.client.smartapps_api.sync_smartapp_event import (
SyncSmartAppEventResponsePayload,
)
from pybotx.models.commands import BotCommand
from pybotx.models.message.incoming_message import IncomingMessage
from pybotx.models.status import StatusRecipient
from pybotx.models.sync_smartapp_event import BotAPISyncSmartAppEventResponse
from pybotx.models.system_events.added_to_chat import AddedToChatEvent
from pybotx.models.system_events.chat_created import ChatCreatedEvent
from pybotx.models.system_events.chat_deleted_by_user import ChatDeletedByUserEvent
Expand All @@ -29,7 +27,7 @@

SyncSmartAppEventHandlerFunc = Callable[
[SmartAppEvent, "Bot"],
Awaitable[SyncSmartAppEventResponsePayload],
Awaitable[BotAPISyncSmartAppEventResponse],
]

IncomingMessageHandlerFunc = HandlerFunc[IncomingMessage]
Expand Down
6 changes: 2 additions & 4 deletions pybotx/bot/handler_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,12 @@
ExceptionMiddleware,
)
from pybotx.client.smartapps_api.exceptions import SyncSmartAppEventHandlerNotFoundError
from pybotx.client.smartapps_api.sync_smartapp_event import (
SyncSmartAppEventResponsePayload,
)
from pybotx.converters import optional_sequence_to_list
from pybotx.logger import logger
from pybotx.models.commands import BotCommand, SystemEvent
from pybotx.models.message.incoming_message import IncomingMessage
from pybotx.models.status import BotMenu, StatusRecipient
from pybotx.models.sync_smartapp_event import BotAPISyncSmartAppEventResponse
from pybotx.models.system_events.added_to_chat import AddedToChatEvent
from pybotx.models.system_events.chat_created import ChatCreatedEvent
from pybotx.models.system_events.chat_deleted_by_user import ChatDeletedByUserEvent
Expand Down Expand Up @@ -126,7 +124,7 @@ async def handle_sync_smartapp_event(
self,
bot: "Bot",
smartapp_event: SmartAppEvent,
) -> SyncSmartAppEventResponsePayload:
) -> BotAPISyncSmartAppEventResponse:
if not isinstance(smartapp_event, SmartAppEvent):
raise NotImplementedError(
f"Unsupported event type for sync smartapp event: `{smartapp_event}`",
Expand Down
11 changes: 3 additions & 8 deletions pybotx/client/smartapps_api/smartapp_event.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict, List, Literal, Type, TypeVar
from typing import Any, Dict, List, Literal
from uuid import UUID

from pybotx.client.authorized_botx_method import AuthorizedBotXMethod
Expand All @@ -7,11 +7,6 @@
from pybotx.models.api_base import UnverifiedPayloadBaseModel, VerifiedPayloadBaseModel
from pybotx.models.async_files import APIAsyncFile, File, convert_async_file_from_domain

TBotXAPISmartAppEventRequestPayload = TypeVar(
"TBotXAPISmartAppEventRequestPayload",
bound="BotXAPISmartAppEventRequestPayload",
)


class BotXAPISmartAppEventRequestPayload(UnverifiedPayloadBaseModel):
ref: MissingOptional[UUID]
Expand All @@ -25,15 +20,15 @@ class BotXAPISmartAppEventRequestPayload(UnverifiedPayloadBaseModel):

@classmethod
def from_domain(
cls: Type[TBotXAPISmartAppEventRequestPayload],
cls,
ref: MissingOptional[UUID],
smartapp_id: UUID,
chat_id: UUID,
data: Dict[str, Any],
opts: Missing[Dict[str, Any]],
files: Missing[List[File]],
encrypted: bool,
) -> TBotXAPISmartAppEventRequestPayload:
) -> "BotXAPISmartAppEventRequestPayload":
api_async_files: Missing[List[APIAsyncFile]] = Undefined
if files:
api_async_files = [convert_async_file_from_domain(file) for file in files]
Expand Down
7 changes: 0 additions & 7 deletions pybotx/client/smartapps_api/sync_smartapp_event.py

This file was deleted.

9 changes: 9 additions & 0 deletions pybotx/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ def trim_file_data_in_incoming_json(json_body: Dict[str, Any]) -> Dict[str, Any]
return json_body


def log_incoming_request(request: Dict[str, Any], *, message: str = "") -> None:
logger.opt(lazy=True).debug(
message + "{command}",
command=lambda: pformat_jsonable_obj(
trim_file_data_in_incoming_json(request),
),
)


def setup_logger() -> "Logger":
return _logger

Expand Down
3 changes: 2 additions & 1 deletion pybotx/models/bot_account.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from functools import cached_property
from typing import Optional
from urllib.parse import urlparse
from uuid import UUID

Expand All @@ -9,7 +10,7 @@
@dataclass
class BotAccount:
id: UUID
host: str
host: Optional[str]


class BotAccountWithSecret(BaseModel):
Expand Down
Loading

0 comments on commit bf2c157

Please sign in to comment.