Skip to content

Commit c230421

Browse files
committed
feat(alias): add async support for alias operations
- add asyncalias class for async individual alias operations - add asyncaliases class for async alias collection operations - add async tests for alias and aliases functionality - add async fixtures for testing async alias operations - remove future annotations imports from test files
1 parent 9c2e2ae commit c230421

5 files changed

Lines changed: 392 additions & 2 deletions

File tree

src/typesense/async_alias.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
This module provides async functionality for managing individual aliases in Typesense.
3+
4+
It contains the AsyncAlias class, which allows for retrieving and deleting
5+
aliases asynchronously.
6+
7+
Classes:
8+
AsyncAlias: Manages async operations on a single alias in the Typesense API.
9+
10+
Dependencies:
11+
- typesense.async_api_call: Provides the AsyncApiCall class for making async API requests.
12+
- typesense.types.alias: Provides AliasSchema type.
13+
14+
Note: This module uses conditional imports to support both Python 3.11+ and earlier versions.
15+
"""
16+
17+
from typesense.async_api_call import AsyncApiCall
18+
from typesense.types.alias import AliasSchema
19+
20+
21+
class AsyncAlias:
22+
"""
23+
Manages async operations on a single alias in the Typesense API.
24+
25+
This class provides async methods to retrieve and delete an alias.
26+
27+
Attributes:
28+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
29+
name (str): The name of the alias.
30+
"""
31+
32+
def __init__(self, api_call: AsyncApiCall, name: str):
33+
"""
34+
Initialize the AsyncAlias instance.
35+
36+
Args:
37+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
38+
name (str): The name of the alias.
39+
"""
40+
self.api_call = api_call
41+
self.name = name
42+
43+
async def retrieve(self) -> AliasSchema:
44+
"""
45+
Retrieve this specific alias.
46+
47+
Returns:
48+
AliasSchema: The schema containing the alias details.
49+
"""
50+
response: AliasSchema = await self.api_call.get(
51+
self._endpoint_path,
52+
entity_type=AliasSchema,
53+
as_json=True,
54+
)
55+
return response
56+
57+
async def delete(self) -> AliasSchema:
58+
"""
59+
Delete this specific alias.
60+
61+
Returns:
62+
AliasSchema: The schema containing the deletion response.
63+
"""
64+
response: AliasSchema = await self.api_call.delete(
65+
self._endpoint_path,
66+
entity_type=AliasSchema,
67+
)
68+
return response
69+
70+
@property
71+
def _endpoint_path(self) -> str:
72+
"""
73+
Construct the API endpoint path for this specific alias.
74+
75+
Returns:
76+
str: The constructed endpoint path.
77+
"""
78+
from typesense.async_aliases import AsyncAliases
79+
80+
return "/".join([AsyncAliases.resource_path, self.name])

src/typesense/async_aliases.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""
2+
This module provides async functionality for managing aliases in Typesense.
3+
4+
It contains the AsyncAliases class, which allows for creating, updating, retrieving, and
5+
accessing individual aliases asynchronously.
6+
7+
Classes:
8+
AsyncAliases: Manages aliases in the Typesense API (async).
9+
10+
Dependencies:
11+
- typesense.async_api_call: Provides the AsyncApiCall class for making async API requests.
12+
- typesense.async_alias: Provides the AsyncAlias class for individual alias operations.
13+
- typesense.types.alias: Provides AliasCreateSchema, AliasSchema, and AliasesResponseSchema types.
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.async_alias import AsyncAlias
22+
from typesense.types.alias import AliasCreateSchema, AliasSchema, AliasesResponseSchema
23+
24+
if sys.version_info >= (3, 11):
25+
import typing
26+
else:
27+
import typing_extensions as typing
28+
29+
30+
class AsyncAliases:
31+
"""
32+
Manages aliases in the Typesense API (async).
33+
34+
This class provides async methods to create, update, retrieve, and access individual aliases.
35+
36+
Attributes:
37+
resource_path (str): The API endpoint path for alias operations.
38+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
39+
aliases (Dict[str, AsyncAlias]): A dictionary of AsyncAlias instances, keyed by alias name.
40+
"""
41+
42+
resource_path: typing.Final[str] = "/aliases"
43+
44+
def __init__(self, api_call: AsyncApiCall):
45+
"""
46+
Initialize the AsyncAliases instance.
47+
48+
Args:
49+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
50+
"""
51+
self.api_call = api_call
52+
self.aliases: typing.Dict[str, AsyncAlias] = {}
53+
54+
def __getitem__(self, name: str) -> AsyncAlias:
55+
"""
56+
Get or create an AsyncAlias instance for a given alias name.
57+
58+
This method allows accessing aliases using dictionary-like syntax.
59+
If the AsyncAlias instance doesn't exist, it creates a new one.
60+
61+
Args:
62+
name (str): The name of the alias.
63+
64+
Returns:
65+
AsyncAlias: The AsyncAlias instance for the specified alias name.
66+
67+
Example:
68+
>>> aliases = AsyncAliases(async_api_call)
69+
>>> company_alias = aliases["company_alias"]
70+
"""
71+
if not self.aliases.get(name):
72+
self.aliases[name] = AsyncAlias(self.api_call, name)
73+
return self.aliases.get(name)
74+
75+
async def upsert(self, name: str, mapping: AliasCreateSchema) -> AliasSchema:
76+
"""
77+
Create or update an alias.
78+
79+
Args:
80+
name (str): The name of the alias.
81+
mapping (AliasCreateSchema): The schema for creating or updating the alias.
82+
83+
Returns:
84+
AliasSchema: The created or updated alias.
85+
86+
Example:
87+
>>> aliases = AsyncAliases(async_api_call)
88+
>>> alias = await aliases.upsert(
89+
... "company_alias", {"collection_name": "companies"}
90+
... )
91+
"""
92+
response: AliasSchema = await self.api_call.put(
93+
self._endpoint_path(name),
94+
body=mapping,
95+
entity_type=AliasSchema,
96+
)
97+
return response
98+
99+
async def retrieve(self) -> AliasesResponseSchema:
100+
"""
101+
Retrieve all aliases.
102+
103+
Returns:
104+
AliasesResponseSchema: The schema containing all aliases.
105+
106+
Example:
107+
>>> aliases = AsyncAliases(async_api_call)
108+
>>> all_aliases = await aliases.retrieve()
109+
>>> for alias in all_aliases["aliases"]:
110+
... print(alias["name"])
111+
"""
112+
response: AliasesResponseSchema = await self.api_call.get(
113+
AsyncAliases.resource_path,
114+
as_json=True,
115+
entity_type=AliasesResponseSchema,
116+
)
117+
return response
118+
119+
def _endpoint_path(self, alias_name: str) -> str:
120+
"""
121+
Construct the API endpoint path for alias operations.
122+
123+
Args:
124+
alias_name (str): The name of the alias.
125+
126+
Returns:
127+
str: The constructed endpoint path.
128+
"""
129+
return "/".join([AsyncAliases.resource_path, alias_name])

tests/alias_test.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Tests for the Alias class."""
22

3-
from __future__ import annotations
43
from tests.utils.object_assertions import (
54
assert_match_object,
65
assert_object_lists_match,
@@ -9,6 +8,9 @@
98
from typesense.alias import Alias
109
from typesense.aliases import Aliases
1110
from typesense.api_call import ApiCall
11+
from typesense.async_api_call import AsyncApiCall
12+
from typesense.async_alias import AsyncAlias
13+
from typesense.async_aliases import AsyncAliases
1214

1315

1416
def test_init(fake_api_call: ApiCall) -> None:
@@ -28,6 +30,23 @@ def test_init(fake_api_call: ApiCall) -> None:
2830
assert alias._endpoint_path == "/aliases/company_alias" # noqa: WPS437
2931

3032

33+
def test_init_async(fake_async_api_call: AsyncApiCall) -> None:
34+
"""Test that the AsyncAlias object is initialized correctly."""
35+
alias = AsyncAlias(fake_async_api_call, "company_alias")
36+
37+
assert alias.name == "company_alias"
38+
assert_match_object(alias.api_call, fake_async_api_call)
39+
assert_object_lists_match(
40+
alias.api_call.node_manager.nodes,
41+
fake_async_api_call.node_manager.nodes,
42+
)
43+
assert_match_object(
44+
alias.api_call.config.nearest_node,
45+
fake_async_api_call.config.nearest_node,
46+
)
47+
assert alias._endpoint_path == "/aliases/company_alias" # noqa: WPS437
48+
49+
3150
def test_actual_retrieve(
3251
actual_aliases: Aliases,
3352
delete_all_aliases: None,
@@ -62,3 +81,39 @@ def test_actual_delete(
6281
"collection_name": "companies",
6382
"name": "company_alias",
6483
}
84+
85+
86+
async def test_actual_retrieve_async(
87+
actual_async_aliases: AsyncAliases,
88+
delete_all_aliases: None,
89+
delete_all: None,
90+
create_alias: None,
91+
) -> None:
92+
"""Test that the AsyncAlias object can retrieve an alias from Typesense Server."""
93+
response = await actual_async_aliases["company_alias"].retrieve()
94+
95+
assert response["collection_name"] == "companies"
96+
assert response["name"] == "company_alias"
97+
98+
assert_to_contain_object(
99+
response,
100+
{
101+
"collection_name": "companies",
102+
"name": "company_alias",
103+
},
104+
)
105+
106+
107+
async def test_actual_delete_async(
108+
actual_async_aliases: AsyncAliases,
109+
delete_all_aliases: None,
110+
delete_all: None,
111+
create_alias: None,
112+
) -> None:
113+
"""Test that the AsyncAlias object can delete an alias from Typesense Server."""
114+
response = await actual_async_aliases["company_alias"].delete()
115+
116+
assert response == {
117+
"collection_name": "companies",
118+
"name": "company_alias",
119+
}

0 commit comments

Comments
 (0)