Skip to content

Commit cff9373

Browse files
committed
feat(ops): add async support for operations
- add AsyncOperations class for async operations functionality - add async tests for operations functionality - add async fixtures for testing async operations
1 parent 6a6e4d3 commit cff9373

File tree

3 files changed

+374
-1
lines changed

3 files changed

+374
-1
lines changed

src/typesense/async_operations.py

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
"""
2+
This module provides async functionality for performing various operations in the Typesense API.
3+
4+
It contains the AsyncOperations class, which handles different API operations such as
5+
health checks, snapshots, and configuration changes asynchronously.
6+
7+
Classes:
8+
AsyncOperations: Manages various async operations in the Typesense API.
9+
10+
Dependencies:
11+
- typesense.types.operations:
12+
Provides type definitions for operation responses and parameters.
13+
- typesense.async_api_call: Provides the AsyncApiCall class for making async API requests.
14+
15+
Note: This module uses conditional imports to support both Python 3.11+ and earlier versions.
16+
"""
17+
18+
import sys
19+
20+
from typesense.async_api_call import AsyncApiCall
21+
from typesense.types.operations import (
22+
HealthCheckResponse,
23+
LogSlowRequestsTimeParams,
24+
OperationResponse,
25+
SchemaChangesResponse,
26+
SnapshotParameters,
27+
)
28+
29+
if sys.version_info >= (3, 11):
30+
import typing
31+
else:
32+
import typing_extensions as typing
33+
34+
35+
class AsyncOperations:
36+
"""
37+
Manages various async operations in the Typesense API.
38+
39+
This class provides async methods to perform different operations such as
40+
health checks, snapshots, and configuration changes.
41+
42+
Attributes:
43+
resource_path (str): The base path for operations endpoints.
44+
health_path (str): The path for the health check endpoint.
45+
config_path (str): The path for the configuration endpoint.
46+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
47+
"""
48+
49+
resource_path: typing.Final[str] = "/operations"
50+
health_path: typing.Final[str] = "/health"
51+
config_path: typing.Final[str] = "/config"
52+
schema_changes: typing.Final[str] = "/schema_changes"
53+
54+
def __init__(self, api_call: AsyncApiCall):
55+
"""
56+
Initialize the AsyncOperations instance.
57+
58+
Args:
59+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
60+
"""
61+
self.api_call = api_call
62+
63+
@typing.overload
64+
async def perform(
65+
self,
66+
operation_name: typing.Literal["schema_changes"],
67+
query_params: None = None,
68+
) -> typing.List[SchemaChangesResponse]:
69+
"""
70+
Perform a schema_changes operation.
71+
72+
Args:
73+
operation_name (Literal["schema_changes"]): The name of the operation.
74+
query_params (None, optional): Query parameters (not used for schema_changes operation).
75+
76+
Returns:
77+
List[SchemaChangesResponse]: The response from the schema_changes operation.
78+
"""
79+
80+
@typing.overload
81+
async def perform(
82+
self,
83+
operation_name: typing.Literal["vote"],
84+
query_params: None = None,
85+
) -> OperationResponse:
86+
"""
87+
Perform a vote operation.
88+
89+
Args:
90+
operation_name (Literal["vote"]): The name of the operation.
91+
query_params (None, optional): Query parameters (not used for vote operation).
92+
93+
Returns:
94+
OperationResponse: The response from the vote operation.
95+
"""
96+
97+
@typing.overload
98+
async def perform(
99+
self,
100+
operation_name: typing.Literal["db/compact"],
101+
query_params: None = None,
102+
) -> OperationResponse:
103+
"""
104+
Perform a database compaction operation.
105+
106+
Args:
107+
operation_name (Literal["db/compact"]): The name of the operation.
108+
query_params (None, optional): Query parameters (not used for db/compact operation).
109+
110+
Returns:
111+
OperationResponse: The response from the database compaction operation.
112+
"""
113+
114+
@typing.overload
115+
async def perform(
116+
self,
117+
operation_name: typing.Literal["cache/clear"],
118+
query_params: None = None,
119+
) -> OperationResponse:
120+
"""
121+
Perform a cache clear operation.
122+
123+
Args:
124+
operation_name (Literal["cache/clear"]): The name of the operation.
125+
query_params (None, optional):
126+
Query parameters (not used for cache/clear operation).
127+
128+
Returns:
129+
OperationResponse: The response from the cache clear operation.
130+
"""
131+
132+
@typing.overload
133+
async def perform(
134+
self,
135+
operation_name: str,
136+
query_params: typing.Union[typing.Dict[str, str], None] = None,
137+
) -> OperationResponse:
138+
"""
139+
Perform a generic operation.
140+
141+
Args:
142+
operation_name (str): The name of the operation.
143+
query_params (Union[Dict[str, str], None], optional):
144+
Query parameters for the operation.
145+
146+
Returns:
147+
OperationResponse: The response from the operation.
148+
"""
149+
150+
@typing.overload
151+
async def perform(
152+
self,
153+
operation_name: typing.Literal["snapshot"],
154+
query_params: SnapshotParameters,
155+
) -> OperationResponse:
156+
"""
157+
Perform a snapshot operation.
158+
159+
Args:
160+
operation_name (Literal["snapshot"]): The name of the operation.
161+
query_params (SnapshotParameters): Query parameters for the snapshot operation.
162+
163+
Returns:
164+
OperationResponse: The response from the snapshot operation.
165+
"""
166+
167+
async def perform(
168+
self,
169+
operation_name: typing.Union[
170+
typing.Literal[
171+
"snapshot",
172+
"vote",
173+
"db/compact",
174+
"cache/clear",
175+
"schema_changes",
176+
],
177+
str,
178+
],
179+
query_params: typing.Union[
180+
SnapshotParameters,
181+
typing.Dict[str, str],
182+
None,
183+
] = None,
184+
) -> OperationResponse:
185+
"""
186+
Perform an operation on the Typesense API.
187+
188+
This method is the actual implementation for all the overloaded perform methods.
189+
190+
Args:
191+
operation_name (Literal["snapshot, vote, db/compact, cache/clear, schema_changes"]):
192+
The name of the operation to perform.
193+
query_params (Union[SnapshotParameters, Dict[str, str], None], optional):
194+
Query parameters for the operation.
195+
196+
Returns:
197+
Union[OperationResponse, List[SchemaChangesResponse]]:
198+
The response from the performed operation.
199+
200+
Example:
201+
>>> operations = AsyncOperations(async_api_call)
202+
>>> response = await operations.perform("vote")
203+
>>> health = await operations.is_healthy()
204+
"""
205+
response: OperationResponse = await self.api_call.post(
206+
self._endpoint_path(operation_name),
207+
params=query_params,
208+
as_json=True,
209+
entity_type=OperationResponse,
210+
)
211+
return response
212+
213+
async def is_healthy(self) -> bool:
214+
"""
215+
Check if the Typesense server is healthy.
216+
217+
Returns:
218+
bool: True if the server is healthy, False otherwise.
219+
220+
Example:
221+
>>> operations = AsyncOperations(async_api_call)
222+
>>> healthy = await operations.is_healthy()
223+
>>> print(healthy)
224+
"""
225+
call_resp: HealthCheckResponse = await self.api_call.get(
226+
AsyncOperations.health_path,
227+
as_json=True,
228+
entity_type=HealthCheckResponse,
229+
)
230+
if isinstance(call_resp, typing.Dict):
231+
is_ok: bool = call_resp.get("ok", False)
232+
else:
233+
is_ok = False
234+
return is_ok
235+
236+
async def toggle_slow_request_log(
237+
self,
238+
log_slow_requests_time_params: LogSlowRequestsTimeParams,
239+
) -> typing.Dict[str, typing.Union[str, bool]]:
240+
"""
241+
Toggle the slow request log configuration.
242+
243+
Args:
244+
log_slow_requests_time_params (LogSlowRequestsTimeParams):
245+
Parameters for configuring slow request logging.
246+
247+
Returns:
248+
Dict[str, Union[str, bool]]: The response from the configuration change operation.
249+
250+
Example:
251+
>>> operations = AsyncOperations(async_api_call)
252+
>>> response = await operations.toggle_slow_request_log(
253+
... {"log_slow_requests_time_ms": 100}
254+
... )
255+
"""
256+
data_dashed = {
257+
key.replace("_", "-"): dashed_value
258+
for key, dashed_value in log_slow_requests_time_params.items()
259+
}
260+
response: typing.Dict[str, typing.Union[str, bool]] = await self.api_call.post(
261+
AsyncOperations.config_path,
262+
as_json=True,
263+
entity_type=typing.Dict[str, typing.Union[str, bool]],
264+
body=data_dashed,
265+
)
266+
return response
267+
268+
@staticmethod
269+
def _endpoint_path(operation_name: str) -> str:
270+
"""
271+
Generate the endpoint path for a given operation.
272+
273+
Args:
274+
operation_name (str): The name of the operation.
275+
276+
Returns:
277+
str: The full endpoint path for the operation.
278+
"""
279+
return "/".join([AsyncOperations.resource_path, operation_name])

tests/fixtures/operation_fixtures.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import pytest
44

55
from typesense.api_call import ApiCall
6+
from typesense.async_api_call import AsyncApiCall
7+
from typesense.async_operations import AsyncOperations
68
from typesense.operations import Operations
79

810

@@ -16,3 +18,19 @@ def actual_operations_fixture(actual_api_call: ApiCall) -> Operations:
1618
def fake_operations_fixture(fake_api_call: ApiCall) -> Operations:
1719
"""Return a Collection object with test values."""
1820
return Operations(fake_api_call)
21+
22+
23+
@pytest.fixture(scope="function", name="actual_async_operations")
24+
def actual_async_operations_fixture(
25+
actual_async_api_call: AsyncApiCall,
26+
) -> AsyncOperations:
27+
"""Return a AsyncOperations object using a real API."""
28+
return AsyncOperations(actual_async_api_call)
29+
30+
31+
@pytest.fixture(scope="function", name="fake_async_operations")
32+
def fake_async_operations_fixture(
33+
fake_async_api_call: AsyncApiCall,
34+
) -> AsyncOperations:
35+
"""Return a AsyncOperations object with test values."""
36+
return AsyncOperations(fake_async_api_call)

0 commit comments

Comments
 (0)