wappa 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wappa might be problematic. Click here for more details.
- wappa/__init__.py +85 -0
- wappa/api/__init__.py +1 -0
- wappa/api/controllers/__init__.py +10 -0
- wappa/api/controllers/webhook_controller.py +441 -0
- wappa/api/dependencies/__init__.py +15 -0
- wappa/api/dependencies/whatsapp_dependencies.py +220 -0
- wappa/api/dependencies/whatsapp_media_dependencies.py +26 -0
- wappa/api/middleware/__init__.py +7 -0
- wappa/api/middleware/error_handler.py +158 -0
- wappa/api/middleware/owner.py +99 -0
- wappa/api/middleware/request_logging.py +184 -0
- wappa/api/routes/__init__.py +6 -0
- wappa/api/routes/health.py +102 -0
- wappa/api/routes/webhooks.py +211 -0
- wappa/api/routes/whatsapp/__init__.py +15 -0
- wappa/api/routes/whatsapp/whatsapp_interactive.py +429 -0
- wappa/api/routes/whatsapp/whatsapp_media.py +440 -0
- wappa/api/routes/whatsapp/whatsapp_messages.py +195 -0
- wappa/api/routes/whatsapp/whatsapp_specialized.py +516 -0
- wappa/api/routes/whatsapp/whatsapp_templates.py +431 -0
- wappa/api/routes/whatsapp_combined.py +35 -0
- wappa/cli/__init__.py +9 -0
- wappa/cli/main.py +199 -0
- wappa/core/__init__.py +6 -0
- wappa/core/config/__init__.py +5 -0
- wappa/core/config/settings.py +161 -0
- wappa/core/events/__init__.py +41 -0
- wappa/core/events/default_handlers.py +642 -0
- wappa/core/events/event_dispatcher.py +244 -0
- wappa/core/events/event_handler.py +247 -0
- wappa/core/events/webhook_factory.py +219 -0
- wappa/core/factory/__init__.py +15 -0
- wappa/core/factory/plugin.py +68 -0
- wappa/core/factory/wappa_builder.py +326 -0
- wappa/core/logging/__init__.py +5 -0
- wappa/core/logging/context.py +100 -0
- wappa/core/logging/logger.py +343 -0
- wappa/core/plugins/__init__.py +34 -0
- wappa/core/plugins/auth_plugin.py +169 -0
- wappa/core/plugins/cors_plugin.py +128 -0
- wappa/core/plugins/custom_middleware_plugin.py +182 -0
- wappa/core/plugins/database_plugin.py +235 -0
- wappa/core/plugins/rate_limit_plugin.py +183 -0
- wappa/core/plugins/redis_plugin.py +224 -0
- wappa/core/plugins/wappa_core_plugin.py +261 -0
- wappa/core/plugins/webhook_plugin.py +253 -0
- wappa/core/types.py +108 -0
- wappa/core/wappa_app.py +546 -0
- wappa/database/__init__.py +18 -0
- wappa/database/adapter.py +107 -0
- wappa/database/adapters/__init__.py +17 -0
- wappa/database/adapters/mysql_adapter.py +187 -0
- wappa/database/adapters/postgresql_adapter.py +169 -0
- wappa/database/adapters/sqlite_adapter.py +174 -0
- wappa/domain/__init__.py +28 -0
- wappa/domain/builders/__init__.py +5 -0
- wappa/domain/builders/message_builder.py +189 -0
- wappa/domain/entities/__init__.py +5 -0
- wappa/domain/enums/messenger_platform.py +123 -0
- wappa/domain/factories/__init__.py +6 -0
- wappa/domain/factories/media_factory.py +450 -0
- wappa/domain/factories/message_factory.py +497 -0
- wappa/domain/factories/messenger_factory.py +244 -0
- wappa/domain/interfaces/__init__.py +32 -0
- wappa/domain/interfaces/base_repository.py +94 -0
- wappa/domain/interfaces/cache_factory.py +85 -0
- wappa/domain/interfaces/cache_interface.py +199 -0
- wappa/domain/interfaces/expiry_repository.py +68 -0
- wappa/domain/interfaces/media_interface.py +311 -0
- wappa/domain/interfaces/messaging_interface.py +523 -0
- wappa/domain/interfaces/pubsub_repository.py +151 -0
- wappa/domain/interfaces/repository_factory.py +108 -0
- wappa/domain/interfaces/shared_state_repository.py +122 -0
- wappa/domain/interfaces/state_repository.py +123 -0
- wappa/domain/interfaces/tables_repository.py +215 -0
- wappa/domain/interfaces/user_repository.py +114 -0
- wappa/domain/interfaces/webhooks/__init__.py +1 -0
- wappa/domain/models/media_result.py +110 -0
- wappa/domain/models/platforms/__init__.py +15 -0
- wappa/domain/models/platforms/platform_config.py +104 -0
- wappa/domain/services/__init__.py +11 -0
- wappa/domain/services/tenant_credentials_service.py +56 -0
- wappa/messaging/__init__.py +7 -0
- wappa/messaging/whatsapp/__init__.py +1 -0
- wappa/messaging/whatsapp/client/__init__.py +5 -0
- wappa/messaging/whatsapp/client/whatsapp_client.py +417 -0
- wappa/messaging/whatsapp/handlers/__init__.py +13 -0
- wappa/messaging/whatsapp/handlers/whatsapp_interactive_handler.py +653 -0
- wappa/messaging/whatsapp/handlers/whatsapp_media_handler.py +579 -0
- wappa/messaging/whatsapp/handlers/whatsapp_specialized_handler.py +434 -0
- wappa/messaging/whatsapp/handlers/whatsapp_template_handler.py +416 -0
- wappa/messaging/whatsapp/messenger/__init__.py +5 -0
- wappa/messaging/whatsapp/messenger/whatsapp_messenger.py +904 -0
- wappa/messaging/whatsapp/models/__init__.py +61 -0
- wappa/messaging/whatsapp/models/basic_models.py +65 -0
- wappa/messaging/whatsapp/models/interactive_models.py +287 -0
- wappa/messaging/whatsapp/models/media_models.py +215 -0
- wappa/messaging/whatsapp/models/specialized_models.py +304 -0
- wappa/messaging/whatsapp/models/template_models.py +261 -0
- wappa/persistence/cache_factory.py +93 -0
- wappa/persistence/json/__init__.py +14 -0
- wappa/persistence/json/cache_adapters.py +271 -0
- wappa/persistence/json/handlers/__init__.py +1 -0
- wappa/persistence/json/handlers/state_handler.py +250 -0
- wappa/persistence/json/handlers/table_handler.py +263 -0
- wappa/persistence/json/handlers/user_handler.py +213 -0
- wappa/persistence/json/handlers/utils/__init__.py +1 -0
- wappa/persistence/json/handlers/utils/file_manager.py +153 -0
- wappa/persistence/json/handlers/utils/key_factory.py +11 -0
- wappa/persistence/json/handlers/utils/serialization.py +121 -0
- wappa/persistence/json/json_cache_factory.py +76 -0
- wappa/persistence/json/storage_manager.py +285 -0
- wappa/persistence/memory/__init__.py +14 -0
- wappa/persistence/memory/cache_adapters.py +271 -0
- wappa/persistence/memory/handlers/__init__.py +1 -0
- wappa/persistence/memory/handlers/state_handler.py +250 -0
- wappa/persistence/memory/handlers/table_handler.py +280 -0
- wappa/persistence/memory/handlers/user_handler.py +213 -0
- wappa/persistence/memory/handlers/utils/__init__.py +1 -0
- wappa/persistence/memory/handlers/utils/key_factory.py +11 -0
- wappa/persistence/memory/handlers/utils/memory_store.py +317 -0
- wappa/persistence/memory/handlers/utils/ttl_manager.py +235 -0
- wappa/persistence/memory/memory_cache_factory.py +76 -0
- wappa/persistence/memory/storage_manager.py +235 -0
- wappa/persistence/redis/README.md +699 -0
- wappa/persistence/redis/__init__.py +11 -0
- wappa/persistence/redis/cache_adapters.py +285 -0
- wappa/persistence/redis/ops.py +880 -0
- wappa/persistence/redis/redis_cache_factory.py +71 -0
- wappa/persistence/redis/redis_client.py +231 -0
- wappa/persistence/redis/redis_handler/__init__.py +26 -0
- wappa/persistence/redis/redis_handler/state_handler.py +176 -0
- wappa/persistence/redis/redis_handler/table.py +158 -0
- wappa/persistence/redis/redis_handler/user.py +138 -0
- wappa/persistence/redis/redis_handler/utils/__init__.py +12 -0
- wappa/persistence/redis/redis_handler/utils/key_factory.py +32 -0
- wappa/persistence/redis/redis_handler/utils/serde.py +146 -0
- wappa/persistence/redis/redis_handler/utils/tenant_cache.py +268 -0
- wappa/persistence/redis/redis_manager.py +189 -0
- wappa/processors/__init__.py +6 -0
- wappa/processors/base_processor.py +262 -0
- wappa/processors/factory.py +550 -0
- wappa/processors/whatsapp_processor.py +810 -0
- wappa/schemas/__init__.py +6 -0
- wappa/schemas/core/__init__.py +71 -0
- wappa/schemas/core/base_message.py +499 -0
- wappa/schemas/core/base_status.py +322 -0
- wappa/schemas/core/base_webhook.py +312 -0
- wappa/schemas/core/types.py +253 -0
- wappa/schemas/core/webhook_interfaces/__init__.py +48 -0
- wappa/schemas/core/webhook_interfaces/base_components.py +293 -0
- wappa/schemas/core/webhook_interfaces/universal_webhooks.py +348 -0
- wappa/schemas/factory.py +754 -0
- wappa/schemas/webhooks/__init__.py +3 -0
- wappa/schemas/whatsapp/__init__.py +6 -0
- wappa/schemas/whatsapp/base_models.py +285 -0
- wappa/schemas/whatsapp/message_types/__init__.py +93 -0
- wappa/schemas/whatsapp/message_types/audio.py +350 -0
- wappa/schemas/whatsapp/message_types/button.py +267 -0
- wappa/schemas/whatsapp/message_types/contact.py +464 -0
- wappa/schemas/whatsapp/message_types/document.py +421 -0
- wappa/schemas/whatsapp/message_types/errors.py +195 -0
- wappa/schemas/whatsapp/message_types/image.py +424 -0
- wappa/schemas/whatsapp/message_types/interactive.py +430 -0
- wappa/schemas/whatsapp/message_types/location.py +416 -0
- wappa/schemas/whatsapp/message_types/order.py +372 -0
- wappa/schemas/whatsapp/message_types/reaction.py +271 -0
- wappa/schemas/whatsapp/message_types/sticker.py +328 -0
- wappa/schemas/whatsapp/message_types/system.py +317 -0
- wappa/schemas/whatsapp/message_types/text.py +411 -0
- wappa/schemas/whatsapp/message_types/unsupported.py +273 -0
- wappa/schemas/whatsapp/message_types/video.py +344 -0
- wappa/schemas/whatsapp/status_models.py +479 -0
- wappa/schemas/whatsapp/validators.py +454 -0
- wappa/schemas/whatsapp/webhook_container.py +438 -0
- wappa/webhooks/__init__.py +17 -0
- wappa/webhooks/core/__init__.py +71 -0
- wappa/webhooks/core/base_message.py +499 -0
- wappa/webhooks/core/base_status.py +322 -0
- wappa/webhooks/core/base_webhook.py +312 -0
- wappa/webhooks/core/types.py +253 -0
- wappa/webhooks/core/webhook_interfaces/__init__.py +48 -0
- wappa/webhooks/core/webhook_interfaces/base_components.py +293 -0
- wappa/webhooks/core/webhook_interfaces/universal_webhooks.py +441 -0
- wappa/webhooks/factory.py +754 -0
- wappa/webhooks/whatsapp/__init__.py +6 -0
- wappa/webhooks/whatsapp/base_models.py +285 -0
- wappa/webhooks/whatsapp/message_types/__init__.py +93 -0
- wappa/webhooks/whatsapp/message_types/audio.py +350 -0
- wappa/webhooks/whatsapp/message_types/button.py +267 -0
- wappa/webhooks/whatsapp/message_types/contact.py +464 -0
- wappa/webhooks/whatsapp/message_types/document.py +421 -0
- wappa/webhooks/whatsapp/message_types/errors.py +195 -0
- wappa/webhooks/whatsapp/message_types/image.py +424 -0
- wappa/webhooks/whatsapp/message_types/interactive.py +430 -0
- wappa/webhooks/whatsapp/message_types/location.py +416 -0
- wappa/webhooks/whatsapp/message_types/order.py +372 -0
- wappa/webhooks/whatsapp/message_types/reaction.py +271 -0
- wappa/webhooks/whatsapp/message_types/sticker.py +328 -0
- wappa/webhooks/whatsapp/message_types/system.py +317 -0
- wappa/webhooks/whatsapp/message_types/text.py +411 -0
- wappa/webhooks/whatsapp/message_types/unsupported.py +273 -0
- wappa/webhooks/whatsapp/message_types/video.py +344 -0
- wappa/webhooks/whatsapp/status_models.py +479 -0
- wappa/webhooks/whatsapp/validators.py +454 -0
- wappa/webhooks/whatsapp/webhook_container.py +438 -0
- wappa-0.1.0.dist-info/METADATA +269 -0
- wappa-0.1.0.dist-info/RECORD +211 -0
- wappa-0.1.0.dist-info/WHEEL +4 -0
- wappa-0.1.0.dist-info/entry_points.txt +2 -0
- wappa-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Redis Module
|
|
3
|
+
|
|
4
|
+
Provides Redis client functionality, operations, and lifecycle management.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from . import ops, redis_handler
|
|
8
|
+
from .redis_client import RedisClient
|
|
9
|
+
from .redis_manager import RedisManager
|
|
10
|
+
|
|
11
|
+
__all__ = ["RedisClient", "RedisManager", "ops", "redis_handler"]
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cache adapters that make Redis handlers implement the ICache interface.
|
|
3
|
+
|
|
4
|
+
These adapters wrap the existing Redis handlers to provide a uniform ICache interface
|
|
5
|
+
while preserving all the existing functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
from ...domain.interfaces.cache_interface import ICache
|
|
13
|
+
from .redis_handler.state_handler import RedisStateHandler
|
|
14
|
+
from .redis_handler.table import RedisTable
|
|
15
|
+
from .redis_handler.user import RedisUser
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RedisStateCacheAdapter(ICache):
|
|
19
|
+
"""Adapter that makes RedisStateHandler implement ICache interface."""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self, tenant_id: str, user_id: str, redis_alias: str = "state_handler"
|
|
23
|
+
):
|
|
24
|
+
self._handler = RedisStateHandler(
|
|
25
|
+
tenant=tenant_id, user_id=user_id, redis_alias=redis_alias
|
|
26
|
+
)
|
|
27
|
+
self._default_handler_name = "cache"
|
|
28
|
+
|
|
29
|
+
async def get(
|
|
30
|
+
self, key: str, models: type[BaseModel] | None = None
|
|
31
|
+
) -> dict[str, Any] | None:
|
|
32
|
+
"""Get cached data by key."""
|
|
33
|
+
return await self._handler.get(key, models=models)
|
|
34
|
+
|
|
35
|
+
async def set(self, key: str, data: dict[str, Any], ttl: int | None = None) -> bool:
|
|
36
|
+
"""Set cached data with optional TTL."""
|
|
37
|
+
return await self._handler.upsert(key, data, ttl=ttl)
|
|
38
|
+
|
|
39
|
+
async def delete(self, key: str) -> bool:
|
|
40
|
+
"""Delete cached data by key."""
|
|
41
|
+
result = await self._handler.delete(key)
|
|
42
|
+
return result > 0
|
|
43
|
+
|
|
44
|
+
async def exists(self, key: str) -> bool:
|
|
45
|
+
"""Check if key exists in cache."""
|
|
46
|
+
return await self._handler.exists(key)
|
|
47
|
+
|
|
48
|
+
async def get_field(self, key: str, field: str) -> Any | None:
|
|
49
|
+
"""Get a specific field from cached hash data."""
|
|
50
|
+
return await self._handler.get_field(key, field)
|
|
51
|
+
|
|
52
|
+
async def set_field(
|
|
53
|
+
self, key: str, field: str, value: Any, ttl: int | None = None
|
|
54
|
+
) -> bool:
|
|
55
|
+
"""Set a specific field in cached hash data."""
|
|
56
|
+
return await self._handler.update_field(key, field, value, ttl=ttl)
|
|
57
|
+
|
|
58
|
+
async def increment_field(
|
|
59
|
+
self, key: str, field: str, increment: int = 1, ttl: int | None = None
|
|
60
|
+
) -> int | None:
|
|
61
|
+
"""Atomically increment an integer field."""
|
|
62
|
+
return await self._handler.increment_field(key, field, increment, ttl=ttl)
|
|
63
|
+
|
|
64
|
+
async def append_to_list(
|
|
65
|
+
self, key: str, field: str, value: Any, ttl: int | None = None
|
|
66
|
+
) -> bool:
|
|
67
|
+
"""Append value to a list field."""
|
|
68
|
+
return await self._handler.append_to_list(key, field, value, ttl=ttl)
|
|
69
|
+
|
|
70
|
+
async def get_ttl(self, key: str) -> int:
|
|
71
|
+
"""Get remaining time to live for a key."""
|
|
72
|
+
handler_key = self._handler._key(key)
|
|
73
|
+
return await self._handler.get_ttl(handler_key)
|
|
74
|
+
|
|
75
|
+
async def set_ttl(self, key: str, ttl: int) -> bool:
|
|
76
|
+
"""Set time to live for a key."""
|
|
77
|
+
handler_key = self._handler._key(key)
|
|
78
|
+
return await self._handler.renew_ttl(handler_key, ttl=ttl)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class RedisUserCacheAdapter(ICache):
|
|
82
|
+
"""Adapter that makes RedisUser implement ICache interface."""
|
|
83
|
+
|
|
84
|
+
def __init__(self, tenant_id: str, user_id: str, redis_alias: str = "users"):
|
|
85
|
+
self._handler = RedisUser(
|
|
86
|
+
tenant=tenant_id, user_id=user_id, redis_alias=redis_alias
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
async def get(
|
|
90
|
+
self, key: str, models: type[BaseModel] | None = None
|
|
91
|
+
) -> dict[str, Any] | None:
|
|
92
|
+
"""Get cached data by key. For user cache, key is ignored as it uses user_id."""
|
|
93
|
+
return await self._handler.get(models=models)
|
|
94
|
+
|
|
95
|
+
async def set(self, key: str, data: dict[str, Any], ttl: int | None = None) -> bool:
|
|
96
|
+
"""Set cached data with optional TTL."""
|
|
97
|
+
return await self._handler.upsert(data, ttl=ttl)
|
|
98
|
+
|
|
99
|
+
async def delete(self, key: str) -> bool:
|
|
100
|
+
"""Delete cached data by key."""
|
|
101
|
+
result = await self._handler.delete()
|
|
102
|
+
return result > 0
|
|
103
|
+
|
|
104
|
+
async def exists(self, key: str) -> bool:
|
|
105
|
+
"""Check if key exists in cache."""
|
|
106
|
+
return await self._handler.exists()
|
|
107
|
+
|
|
108
|
+
async def get_field(self, key: str, field: str) -> Any | None:
|
|
109
|
+
"""Get a specific field from cached hash data."""
|
|
110
|
+
return await self._handler.get_field(field)
|
|
111
|
+
|
|
112
|
+
async def set_field(
|
|
113
|
+
self, key: str, field: str, value: Any, ttl: int | None = None
|
|
114
|
+
) -> bool:
|
|
115
|
+
"""Set a specific field in cached hash data."""
|
|
116
|
+
return await self._handler.update_field(field, value, ttl=ttl)
|
|
117
|
+
|
|
118
|
+
async def increment_field(
|
|
119
|
+
self, key: str, field: str, increment: int = 1, ttl: int | None = None
|
|
120
|
+
) -> int | None:
|
|
121
|
+
"""Atomically increment an integer field."""
|
|
122
|
+
return await self._handler.increment_field(field, increment, ttl=ttl)
|
|
123
|
+
|
|
124
|
+
async def append_to_list(
|
|
125
|
+
self, key: str, field: str, value: Any, ttl: int | None = None
|
|
126
|
+
) -> bool:
|
|
127
|
+
"""Append value to a list field."""
|
|
128
|
+
return await self._handler.append_to_list(field, value, ttl=ttl)
|
|
129
|
+
|
|
130
|
+
async def get_ttl(self, key: str) -> int:
|
|
131
|
+
"""Get remaining time to live for a key."""
|
|
132
|
+
handler_key = self._handler._key()
|
|
133
|
+
return await self._handler.get_ttl(handler_key)
|
|
134
|
+
|
|
135
|
+
async def set_ttl(self, key: str, ttl: int) -> bool:
|
|
136
|
+
"""Set time to live for a key."""
|
|
137
|
+
handler_key = self._handler._key()
|
|
138
|
+
return await self._handler.renew_ttl(handler_key, ttl=ttl)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class RedisTableCacheAdapter(ICache):
|
|
142
|
+
"""
|
|
143
|
+
Adapter that makes RedisTable implement ICache interface.
|
|
144
|
+
|
|
145
|
+
Table Key Format Guide:
|
|
146
|
+
Use create_table_key(table_name, pkid) to generate proper keys.
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
# Good - using helper method
|
|
150
|
+
key = cache.create_table_key("user_profiles", "12345")
|
|
151
|
+
await cache.set(key, user_data)
|
|
152
|
+
|
|
153
|
+
# Also supported - manual format
|
|
154
|
+
key = "user_profiles:12345"
|
|
155
|
+
await cache.set(key, user_data)
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
def __init__(self, tenant_id: str, redis_alias: str = "table"):
|
|
159
|
+
self._handler = RedisTable(tenant=tenant_id, redis_alias=redis_alias)
|
|
160
|
+
|
|
161
|
+
def create_table_key(self, table_name: str, pkid: str) -> str:
|
|
162
|
+
"""
|
|
163
|
+
Create a properly formatted table cache key.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
table_name: Name of the table (e.g., "user_profiles", "message_logs")
|
|
167
|
+
pkid: Primary key ID (e.g., user_id, message_id)
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Formatted key string for use with cache methods
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
key = cache.create_table_key("user_profiles", "12345")
|
|
174
|
+
# Returns: "user_profiles:12345"
|
|
175
|
+
"""
|
|
176
|
+
if not table_name or not pkid:
|
|
177
|
+
raise ValueError("Both table_name and pkid must be provided and non-empty")
|
|
178
|
+
|
|
179
|
+
# Sanitize inputs to avoid conflicts
|
|
180
|
+
safe_table_name = str(table_name).replace(":", "_")
|
|
181
|
+
safe_pkid = str(pkid).replace(":", "_")
|
|
182
|
+
|
|
183
|
+
return f"{safe_table_name}:{safe_pkid}"
|
|
184
|
+
|
|
185
|
+
async def get(
|
|
186
|
+
self, key: str, models: type[BaseModel] | None = None
|
|
187
|
+
) -> dict[str, Any] | None:
|
|
188
|
+
"""Get cached data by key. Key should be in format 'table_name:pkid'."""
|
|
189
|
+
table_name, pkid = self._parse_key(key)
|
|
190
|
+
return await self._handler.get(table_name, pkid, models=models)
|
|
191
|
+
|
|
192
|
+
async def set(self, key: str, data: dict[str, Any], ttl: int | None = None) -> bool:
|
|
193
|
+
"""Set cached data with optional TTL."""
|
|
194
|
+
table_name, pkid = self._parse_key(key)
|
|
195
|
+
return await self._handler.upsert(table_name, pkid, data, ttl=ttl)
|
|
196
|
+
|
|
197
|
+
async def delete(self, key: str) -> bool:
|
|
198
|
+
"""Delete cached data by key."""
|
|
199
|
+
table_name, pkid = self._parse_key(key)
|
|
200
|
+
result = await self._handler.delete(table_name, pkid)
|
|
201
|
+
return result > 0
|
|
202
|
+
|
|
203
|
+
async def exists(self, key: str) -> bool:
|
|
204
|
+
"""Check if key exists in cache."""
|
|
205
|
+
table_name, pkid = self._parse_key(key)
|
|
206
|
+
return await self._handler.exists(table_name, pkid)
|
|
207
|
+
|
|
208
|
+
async def get_field(self, key: str, field: str) -> Any | None:
|
|
209
|
+
"""Get a specific field from cached hash data."""
|
|
210
|
+
table_name, pkid = self._parse_key(key)
|
|
211
|
+
return await self._handler.get_field(table_name, pkid, field)
|
|
212
|
+
|
|
213
|
+
async def set_field(
|
|
214
|
+
self, key: str, field: str, value: Any, ttl: int | None = None
|
|
215
|
+
) -> bool:
|
|
216
|
+
"""Set a specific field in cached hash data."""
|
|
217
|
+
table_name, pkid = self._parse_key(key)
|
|
218
|
+
return await self._handler.update_field(table_name, pkid, field, value, ttl=ttl)
|
|
219
|
+
|
|
220
|
+
async def increment_field(
|
|
221
|
+
self, key: str, field: str, increment: int = 1, ttl: int | None = None
|
|
222
|
+
) -> int | None:
|
|
223
|
+
"""Atomically increment an integer field."""
|
|
224
|
+
table_name, pkid = self._parse_key(key)
|
|
225
|
+
return await self._handler.increment_field(
|
|
226
|
+
table_name, pkid, field, increment, ttl=ttl
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
async def append_to_list(
|
|
230
|
+
self, key: str, field: str, value: Any, ttl: int | None = None
|
|
231
|
+
) -> bool:
|
|
232
|
+
"""Append value to a list field."""
|
|
233
|
+
table_name, pkid = self._parse_key(key)
|
|
234
|
+
return await self._handler.append_to_list(
|
|
235
|
+
table_name, pkid, field, value, ttl=ttl
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
async def get_ttl(self, key: str) -> int:
|
|
239
|
+
"""Get remaining time to live for a key."""
|
|
240
|
+
table_name, pkid = self._parse_key(key)
|
|
241
|
+
handler_key = self._handler._key(table_name, pkid)
|
|
242
|
+
return await self._handler.get_ttl(handler_key)
|
|
243
|
+
|
|
244
|
+
async def set_ttl(self, key: str, ttl: int) -> bool:
|
|
245
|
+
"""Set time to live for a key."""
|
|
246
|
+
table_name, pkid = self._parse_key(key)
|
|
247
|
+
handler_key = self._handler._key(table_name, pkid)
|
|
248
|
+
return await self._handler.renew_ttl(handler_key, ttl=ttl)
|
|
249
|
+
|
|
250
|
+
def _parse_key(self, key: str) -> tuple[str, str]:
|
|
251
|
+
"""
|
|
252
|
+
Parse key into table_name and pkid with validation.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
key: Cache key in format "table_name:pkid"
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Tuple of (table_name, pkid)
|
|
259
|
+
|
|
260
|
+
Raises:
|
|
261
|
+
ValueError: If key format is invalid
|
|
262
|
+
"""
|
|
263
|
+
if not key:
|
|
264
|
+
raise ValueError("Key cannot be empty")
|
|
265
|
+
|
|
266
|
+
if ":" not in key:
|
|
267
|
+
raise ValueError(
|
|
268
|
+
f"Invalid table cache key format: '{key}'. "
|
|
269
|
+
f"Expected format: 'table_name:pkid'. "
|
|
270
|
+
f"Use create_table_key(table_name, pkid) to generate proper keys."
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
parts = key.split(":", 1)
|
|
274
|
+
if len(parts) != 2:
|
|
275
|
+
raise ValueError(f"Invalid table cache key format: '{key}'. Expected exactly one ':' separator.")
|
|
276
|
+
|
|
277
|
+
table_name, pkid = parts
|
|
278
|
+
|
|
279
|
+
if not table_name.strip():
|
|
280
|
+
raise ValueError(f"Invalid table cache key: '{key}'. Table name cannot be empty.")
|
|
281
|
+
|
|
282
|
+
if not pkid.strip():
|
|
283
|
+
raise ValueError(f"Invalid table cache key: '{key}'. Primary key ID cannot be empty.")
|
|
284
|
+
|
|
285
|
+
return table_name.strip(), pkid.strip()
|