Coverage for flogin/jsonrpc/responses.py: 75%
44 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-03 22:51 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-03 22:51 +0000
1from __future__ import annotations
3import json
4from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
6from ..utils import MISSING
7from .base_object import ToMessageBase
8from .enums import ErrorCode
10if TYPE_CHECKING:
11 from .._types.jsonrpc.responses import (
12 ErrorPayload,
13 )
14 from .._types.jsonrpc.responses import (
15 RawErrorResponse as ErrorResponsePayload,
16 )
17 from .._types.jsonrpc.responses import (
18 RawExecuteResponse as ExecuteResponsePayload,
19 )
20 from .._types.jsonrpc.responses import (
21 RawQueryResponse as QueryResponsePayload,
22 )
23 from .results import Result
25T = TypeVar("T")
27__all__ = (
28 "ErrorResponse",
29 "ExecuteResponse",
30 "QueryResponse",
31)
34class BaseResponse(ToMessageBase[T], Generic[T]):
35 r"""This represents a response to flow.
37 .. WARNING::
38 This class is NOT to be used as is. Use one of it's subclasses instead.
39 """
41 def to_message(self, id: int) -> bytes:
42 return (
43 json.dumps(
44 {
45 "jsonrpc": "2.0",
46 "id": id,
47 }
48 | cast("dict[Any, Any]", self.to_dict())
49 )
50 + "\r\n"
51 ).encode()
54class ErrorResponse(BaseResponse["ErrorResponsePayload"]):
55 r"""This represents an error sent to or from flow.
57 Attributes
58 --------
59 code: :class:`int`
60 The error code for the error
61 message: :class:`str`
62 The error's message
63 data: Optional[Any]
64 Any extra data
65 """
67 __slots__ = "code", "data", "message"
69 def __init__(self, code: int, message: str, data: Any | None = None) -> None:
70 self.code = code
71 self.message = message
72 self.data = data
74 def to_dict(self) -> ErrorResponsePayload:
75 data = self.data
76 if isinstance(data, Exception):
77 data = f"{data}"
78 return {"error": {"code": self.code, "message": self.message, "data": data}}
80 @classmethod
81 def from_dict(
82 cls: type[ErrorResponse], data: ErrorPayload | ErrorResponsePayload
83 ) -> ErrorResponse:
84 if "error" in data:
85 data = data["error"]
86 return cls(code=data["code"], message=data["message"], data=data.get("data"))
88 @classmethod
89 def internal_error(cls: type[ErrorResponse], data: Any = None) -> ErrorResponse:
90 return cls(
91 code=ErrorCode.server_error_start.value, message="Internal error", data=data
92 )
95class QueryResponse(BaseResponse["QueryResponsePayload"]):
96 r"""This response represents the response from search handler's callbacks and context menus. See the :ref:`search handler section <search_handlers>` for more information about using search handlers.
98 Attributes
99 --------
100 results: list[:class:`~flogin.jsonrpc.results.Result`]
101 The results to be sent as the result of the query
102 settings_changes: dict[:class:`str`, Any]
103 Any changes to be made to the plugin's settings.
104 debug_message: :class:`str`
105 A debug message if you want
106 """
108 __slots__ = "debug_message", "results", "settings_changes"
110 def __init__(
111 self,
112 results: list[Result],
113 settings_changes: dict[str, Any] | None = None,
114 debug_message: str = MISSING,
115 ) -> None:
116 self.results = results
117 self.settings_changes = settings_changes or {}
118 self.debug_message = debug_message or ""
120 def to_dict(self) -> QueryResponsePayload:
121 return {
122 "result": {
123 "settingsChange": self.settings_changes,
124 "debugMessage": self.debug_message or "",
125 "result": [res.to_dict() for res in self.results],
126 }
127 }
130class ExecuteResponse(BaseResponse["ExecuteResponsePayload"]):
131 r"""This response is a generic response for jsonrpc requests, most notably result callbacks.
133 Attributes
134 --------
135 hide: :class:`bool`
136 Whether to hide the flow menu after execution or not
137 """
139 __slots__ = ("hide",)
141 def __init__(self, hide: bool = True) -> None:
142 self.hide = hide
144 def to_dict(self) -> ExecuteResponsePayload:
145 return {"result": {"hide": self.hide}}