Skip to content

Commit

Permalink
feat: define global rpc errors
Browse files Browse the repository at this point in the history
  • Loading branch information
Kiruha01 committed Jul 1, 2024
1 parent 99db207 commit 24058d1
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 15 deletions.
4 changes: 2 additions & 2 deletions pybotx_smartapp_rpc/models/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class RPCMethod:
response_field: ModelField
arguments_field: Optional[ModelField] = None
tags: List[Union[str, Enum]] = field(default_factory=list)
errors: Optional[Dict[str, dict]] = None
errors_models: Optional[Dict[str, ModelField]] = None
errors: Dict[str, dict] = field(default_factory=dict)
errors_models: Dict[str, ModelField] = field(default_factory=dict)
include_in_schema: bool = True

async def __call__(
Expand Down
2 changes: 1 addition & 1 deletion pybotx_smartapp_rpc/openapi_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def get_rpc_flat_models_from_routes(

if router.rpc_methods[method_name].errors_models:
responses_from_routes.extend(
router.rpc_methods[method_name].errors_models.values(), # type: ignore
router.rpc_methods[method_name].errors_models.values(),
)

return get_flat_models_from_fields(
Expand Down
12 changes: 10 additions & 2 deletions pybotx_smartapp_rpc/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ def __init__(
middlewares: Optional[List[Middleware]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
include_in_schema: bool = True,
errors: Optional[List[Type[RPCError]]] = None,
) -> None:
self.rpc_methods: Dict[str, RPCMethod] = {}
self.middlewares: List[Middleware] = middlewares or []
self.tags: List[Union[str, Enum]] = tags or []
self.include_in_schema = include_in_schema
self.errors: List[Type[RPCError]] = errors or []

def method(
self,
Expand All @@ -56,7 +58,9 @@ def decorator(handler: Handler) -> Handler:
handler,
return_type,
)
errors_fields, errors_models = self._get_error_fields_and_models(errors)
errors_fields, errors_models = self._get_error_fields_and_models(
self.errors + (errors or []),
)

self.rpc_methods[rpc_method_name] = RPCMethod(
handler=handler,
Expand Down Expand Up @@ -103,8 +107,12 @@ def include_router(self, router: "RPCRouter") -> None:
f"RPC methods {already_exist_handlers} already registered!",
)

errors_fields, errors_models = self._get_error_fields_and_models(self.errors)

for rpc_method_name, rpc_method in router.rpc_methods.items():
rpc_method.middlewares = self.middlewares + rpc_method.middlewares
rpc_method.errors = {**errors_fields, **rpc_method.errors}
rpc_method.errors_models = {**errors_models, **rpc_method.errors_models}
self.rpc_methods[rpc_method_name] = rpc_method

def _get_args_and_return_field(
Expand Down Expand Up @@ -145,7 +153,7 @@ def _get_args_and_return_field(
def _get_error_fields_and_models(
self,
errors: Optional[List[Type[RPCError]]],
) -> Tuple[Optional[dict], dict]:
) -> Tuple[dict, dict]:
errors_fields = {}
errors_models = {}
if errors:
Expand Down
16 changes: 11 additions & 5 deletions pybotx_smartapp_rpc/rpc.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from typing import List, Optional
from typing import List, Optional, Type

from pybotx import Bot, SmartAppEvent, SyncSmartAppEventResponsePayload
from pydantic.error_wrappers import ValidationError

from pybotx_smartapp_rpc import RPCError
from pybotx_smartapp_rpc.exception_handlers import (
default_exception_handler,
rpc_exception_handler,
Expand All @@ -19,16 +20,17 @@


class SmartAppRPC:
def __init__(
def __init__( # noqa: WPS234
self,
routers: List[RPCRouter],
middlewares: Optional[List[Middleware]] = None,
exception_handlers: Optional[ExceptionHandlerDict] = None,
errors: Optional[List[Type[RPCError]]] = None,
) -> None:
self._middlewares = middlewares or []
self._insert_exception_middleware(exception_handlers or {})

self._router = self._merge_routers(routers)
self._router = self._merge_routers(routers, errors or [])

async def handle_smartapp_event(self, event: SmartAppEvent, bot: Bot) -> None:
rpc_response: RPCResponse
Expand Down Expand Up @@ -97,8 +99,12 @@ def _insert_exception_middleware(
exc_middleware = ExceptionMiddleware(exception_handlers)
self._middlewares.insert(0, exc_middleware)

def _merge_routers(self, routers: List[RPCRouter]) -> RPCRouter:
main_router = RPCRouter(middlewares=self._middlewares)
def _merge_routers(
self,
routers: List[RPCRouter],
errors: List[Type[RPCError]],
) -> RPCRouter:
main_router = RPCRouter(middlewares=self._middlewares, errors=errors)
main_router.include(*routers)

return main_router
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pybotx-smartapp-rpc"
version = "0.9.0"
version = "0.10.0"
description = "eXpress SmartApp JSON-RPC library"
authors = ["Arseniy Zhiltsov <[email protected]>"]
readme = "README.md"
Expand Down
23 changes: 19 additions & 4 deletions tests/test_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class OneUserNotFound(UserNotFound):
id = "OneUserNotFound"


class InvalidCredentialsError(RPCError):
id = "InvalidCredentialsError"
reason = "Invalid credentials"


def test__deep_dict_update() -> None:
a = {"a": {"b": {"c": 1}}}
b = {"a": {"b": {"d": 2}}}
Expand Down Expand Up @@ -122,15 +127,15 @@ async def get_api_version(smartapp: SmartApp) -> RPCResultResponse[int]:

async def test_collect_rpc_method_exists__with_errors() -> None:
# - Arrange -
rpc = RPCRouter(tags=["rpc"])
rpc = RPCRouter(tags=["rpc"], errors=[InvalidCredentialsError])

@rpc.method("get_user", errors=[UserNotFound, OneUserNotFound], tags=["user"])
@rpc.method("get_user", errors=[UserNotFound], tags=["user"])
async def get_api_version(
smartapp: SmartApp, rpc_args: UserArgs
) -> RPCResultResponse[int]:
return RPCResultResponse(result=42)

smartapp_rpc = SmartAppRPC(routers=[rpc])
smartapp_rpc = SmartAppRPC(routers=[rpc], errors=[OneUserNotFound])
rpc_model_name_map = get_model_name_map(
get_rpc_flat_models_from_routes(smartapp_rpc.router)
)
Expand Down Expand Up @@ -184,6 +189,16 @@ async def get_api_version(
},
"description": "**Error**: User not found in system",
},
"InvalidCredentialsError": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidCredentialsError"
}
}
},
"description": "**Error**: Invalid credentials",
},
},
}
}
Expand All @@ -200,7 +215,7 @@ async def get_api_version(
) -> RPCResultResponse[int]:
return RPCResultResponse(result=42)

smartapp_rpc = SmartAppRPC(routers=[rpc])
smartapp_rpc = SmartAppRPC(routers=[rpc], errors=[])

flat_rpc_models = get_rpc_flat_models_from_routes(smartapp_rpc.router)
rpc_model_name_map = get_model_name_map(flat_rpc_models)
Expand Down

0 comments on commit 24058d1

Please sign in to comment.