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.

Files changed (211) hide show
  1. wappa/__init__.py +85 -0
  2. wappa/api/__init__.py +1 -0
  3. wappa/api/controllers/__init__.py +10 -0
  4. wappa/api/controllers/webhook_controller.py +441 -0
  5. wappa/api/dependencies/__init__.py +15 -0
  6. wappa/api/dependencies/whatsapp_dependencies.py +220 -0
  7. wappa/api/dependencies/whatsapp_media_dependencies.py +26 -0
  8. wappa/api/middleware/__init__.py +7 -0
  9. wappa/api/middleware/error_handler.py +158 -0
  10. wappa/api/middleware/owner.py +99 -0
  11. wappa/api/middleware/request_logging.py +184 -0
  12. wappa/api/routes/__init__.py +6 -0
  13. wappa/api/routes/health.py +102 -0
  14. wappa/api/routes/webhooks.py +211 -0
  15. wappa/api/routes/whatsapp/__init__.py +15 -0
  16. wappa/api/routes/whatsapp/whatsapp_interactive.py +429 -0
  17. wappa/api/routes/whatsapp/whatsapp_media.py +440 -0
  18. wappa/api/routes/whatsapp/whatsapp_messages.py +195 -0
  19. wappa/api/routes/whatsapp/whatsapp_specialized.py +516 -0
  20. wappa/api/routes/whatsapp/whatsapp_templates.py +431 -0
  21. wappa/api/routes/whatsapp_combined.py +35 -0
  22. wappa/cli/__init__.py +9 -0
  23. wappa/cli/main.py +199 -0
  24. wappa/core/__init__.py +6 -0
  25. wappa/core/config/__init__.py +5 -0
  26. wappa/core/config/settings.py +161 -0
  27. wappa/core/events/__init__.py +41 -0
  28. wappa/core/events/default_handlers.py +642 -0
  29. wappa/core/events/event_dispatcher.py +244 -0
  30. wappa/core/events/event_handler.py +247 -0
  31. wappa/core/events/webhook_factory.py +219 -0
  32. wappa/core/factory/__init__.py +15 -0
  33. wappa/core/factory/plugin.py +68 -0
  34. wappa/core/factory/wappa_builder.py +326 -0
  35. wappa/core/logging/__init__.py +5 -0
  36. wappa/core/logging/context.py +100 -0
  37. wappa/core/logging/logger.py +343 -0
  38. wappa/core/plugins/__init__.py +34 -0
  39. wappa/core/plugins/auth_plugin.py +169 -0
  40. wappa/core/plugins/cors_plugin.py +128 -0
  41. wappa/core/plugins/custom_middleware_plugin.py +182 -0
  42. wappa/core/plugins/database_plugin.py +235 -0
  43. wappa/core/plugins/rate_limit_plugin.py +183 -0
  44. wappa/core/plugins/redis_plugin.py +224 -0
  45. wappa/core/plugins/wappa_core_plugin.py +261 -0
  46. wappa/core/plugins/webhook_plugin.py +253 -0
  47. wappa/core/types.py +108 -0
  48. wappa/core/wappa_app.py +546 -0
  49. wappa/database/__init__.py +18 -0
  50. wappa/database/adapter.py +107 -0
  51. wappa/database/adapters/__init__.py +17 -0
  52. wappa/database/adapters/mysql_adapter.py +187 -0
  53. wappa/database/adapters/postgresql_adapter.py +169 -0
  54. wappa/database/adapters/sqlite_adapter.py +174 -0
  55. wappa/domain/__init__.py +28 -0
  56. wappa/domain/builders/__init__.py +5 -0
  57. wappa/domain/builders/message_builder.py +189 -0
  58. wappa/domain/entities/__init__.py +5 -0
  59. wappa/domain/enums/messenger_platform.py +123 -0
  60. wappa/domain/factories/__init__.py +6 -0
  61. wappa/domain/factories/media_factory.py +450 -0
  62. wappa/domain/factories/message_factory.py +497 -0
  63. wappa/domain/factories/messenger_factory.py +244 -0
  64. wappa/domain/interfaces/__init__.py +32 -0
  65. wappa/domain/interfaces/base_repository.py +94 -0
  66. wappa/domain/interfaces/cache_factory.py +85 -0
  67. wappa/domain/interfaces/cache_interface.py +199 -0
  68. wappa/domain/interfaces/expiry_repository.py +68 -0
  69. wappa/domain/interfaces/media_interface.py +311 -0
  70. wappa/domain/interfaces/messaging_interface.py +523 -0
  71. wappa/domain/interfaces/pubsub_repository.py +151 -0
  72. wappa/domain/interfaces/repository_factory.py +108 -0
  73. wappa/domain/interfaces/shared_state_repository.py +122 -0
  74. wappa/domain/interfaces/state_repository.py +123 -0
  75. wappa/domain/interfaces/tables_repository.py +215 -0
  76. wappa/domain/interfaces/user_repository.py +114 -0
  77. wappa/domain/interfaces/webhooks/__init__.py +1 -0
  78. wappa/domain/models/media_result.py +110 -0
  79. wappa/domain/models/platforms/__init__.py +15 -0
  80. wappa/domain/models/platforms/platform_config.py +104 -0
  81. wappa/domain/services/__init__.py +11 -0
  82. wappa/domain/services/tenant_credentials_service.py +56 -0
  83. wappa/messaging/__init__.py +7 -0
  84. wappa/messaging/whatsapp/__init__.py +1 -0
  85. wappa/messaging/whatsapp/client/__init__.py +5 -0
  86. wappa/messaging/whatsapp/client/whatsapp_client.py +417 -0
  87. wappa/messaging/whatsapp/handlers/__init__.py +13 -0
  88. wappa/messaging/whatsapp/handlers/whatsapp_interactive_handler.py +653 -0
  89. wappa/messaging/whatsapp/handlers/whatsapp_media_handler.py +579 -0
  90. wappa/messaging/whatsapp/handlers/whatsapp_specialized_handler.py +434 -0
  91. wappa/messaging/whatsapp/handlers/whatsapp_template_handler.py +416 -0
  92. wappa/messaging/whatsapp/messenger/__init__.py +5 -0
  93. wappa/messaging/whatsapp/messenger/whatsapp_messenger.py +904 -0
  94. wappa/messaging/whatsapp/models/__init__.py +61 -0
  95. wappa/messaging/whatsapp/models/basic_models.py +65 -0
  96. wappa/messaging/whatsapp/models/interactive_models.py +287 -0
  97. wappa/messaging/whatsapp/models/media_models.py +215 -0
  98. wappa/messaging/whatsapp/models/specialized_models.py +304 -0
  99. wappa/messaging/whatsapp/models/template_models.py +261 -0
  100. wappa/persistence/cache_factory.py +93 -0
  101. wappa/persistence/json/__init__.py +14 -0
  102. wappa/persistence/json/cache_adapters.py +271 -0
  103. wappa/persistence/json/handlers/__init__.py +1 -0
  104. wappa/persistence/json/handlers/state_handler.py +250 -0
  105. wappa/persistence/json/handlers/table_handler.py +263 -0
  106. wappa/persistence/json/handlers/user_handler.py +213 -0
  107. wappa/persistence/json/handlers/utils/__init__.py +1 -0
  108. wappa/persistence/json/handlers/utils/file_manager.py +153 -0
  109. wappa/persistence/json/handlers/utils/key_factory.py +11 -0
  110. wappa/persistence/json/handlers/utils/serialization.py +121 -0
  111. wappa/persistence/json/json_cache_factory.py +76 -0
  112. wappa/persistence/json/storage_manager.py +285 -0
  113. wappa/persistence/memory/__init__.py +14 -0
  114. wappa/persistence/memory/cache_adapters.py +271 -0
  115. wappa/persistence/memory/handlers/__init__.py +1 -0
  116. wappa/persistence/memory/handlers/state_handler.py +250 -0
  117. wappa/persistence/memory/handlers/table_handler.py +280 -0
  118. wappa/persistence/memory/handlers/user_handler.py +213 -0
  119. wappa/persistence/memory/handlers/utils/__init__.py +1 -0
  120. wappa/persistence/memory/handlers/utils/key_factory.py +11 -0
  121. wappa/persistence/memory/handlers/utils/memory_store.py +317 -0
  122. wappa/persistence/memory/handlers/utils/ttl_manager.py +235 -0
  123. wappa/persistence/memory/memory_cache_factory.py +76 -0
  124. wappa/persistence/memory/storage_manager.py +235 -0
  125. wappa/persistence/redis/README.md +699 -0
  126. wappa/persistence/redis/__init__.py +11 -0
  127. wappa/persistence/redis/cache_adapters.py +285 -0
  128. wappa/persistence/redis/ops.py +880 -0
  129. wappa/persistence/redis/redis_cache_factory.py +71 -0
  130. wappa/persistence/redis/redis_client.py +231 -0
  131. wappa/persistence/redis/redis_handler/__init__.py +26 -0
  132. wappa/persistence/redis/redis_handler/state_handler.py +176 -0
  133. wappa/persistence/redis/redis_handler/table.py +158 -0
  134. wappa/persistence/redis/redis_handler/user.py +138 -0
  135. wappa/persistence/redis/redis_handler/utils/__init__.py +12 -0
  136. wappa/persistence/redis/redis_handler/utils/key_factory.py +32 -0
  137. wappa/persistence/redis/redis_handler/utils/serde.py +146 -0
  138. wappa/persistence/redis/redis_handler/utils/tenant_cache.py +268 -0
  139. wappa/persistence/redis/redis_manager.py +189 -0
  140. wappa/processors/__init__.py +6 -0
  141. wappa/processors/base_processor.py +262 -0
  142. wappa/processors/factory.py +550 -0
  143. wappa/processors/whatsapp_processor.py +810 -0
  144. wappa/schemas/__init__.py +6 -0
  145. wappa/schemas/core/__init__.py +71 -0
  146. wappa/schemas/core/base_message.py +499 -0
  147. wappa/schemas/core/base_status.py +322 -0
  148. wappa/schemas/core/base_webhook.py +312 -0
  149. wappa/schemas/core/types.py +253 -0
  150. wappa/schemas/core/webhook_interfaces/__init__.py +48 -0
  151. wappa/schemas/core/webhook_interfaces/base_components.py +293 -0
  152. wappa/schemas/core/webhook_interfaces/universal_webhooks.py +348 -0
  153. wappa/schemas/factory.py +754 -0
  154. wappa/schemas/webhooks/__init__.py +3 -0
  155. wappa/schemas/whatsapp/__init__.py +6 -0
  156. wappa/schemas/whatsapp/base_models.py +285 -0
  157. wappa/schemas/whatsapp/message_types/__init__.py +93 -0
  158. wappa/schemas/whatsapp/message_types/audio.py +350 -0
  159. wappa/schemas/whatsapp/message_types/button.py +267 -0
  160. wappa/schemas/whatsapp/message_types/contact.py +464 -0
  161. wappa/schemas/whatsapp/message_types/document.py +421 -0
  162. wappa/schemas/whatsapp/message_types/errors.py +195 -0
  163. wappa/schemas/whatsapp/message_types/image.py +424 -0
  164. wappa/schemas/whatsapp/message_types/interactive.py +430 -0
  165. wappa/schemas/whatsapp/message_types/location.py +416 -0
  166. wappa/schemas/whatsapp/message_types/order.py +372 -0
  167. wappa/schemas/whatsapp/message_types/reaction.py +271 -0
  168. wappa/schemas/whatsapp/message_types/sticker.py +328 -0
  169. wappa/schemas/whatsapp/message_types/system.py +317 -0
  170. wappa/schemas/whatsapp/message_types/text.py +411 -0
  171. wappa/schemas/whatsapp/message_types/unsupported.py +273 -0
  172. wappa/schemas/whatsapp/message_types/video.py +344 -0
  173. wappa/schemas/whatsapp/status_models.py +479 -0
  174. wappa/schemas/whatsapp/validators.py +454 -0
  175. wappa/schemas/whatsapp/webhook_container.py +438 -0
  176. wappa/webhooks/__init__.py +17 -0
  177. wappa/webhooks/core/__init__.py +71 -0
  178. wappa/webhooks/core/base_message.py +499 -0
  179. wappa/webhooks/core/base_status.py +322 -0
  180. wappa/webhooks/core/base_webhook.py +312 -0
  181. wappa/webhooks/core/types.py +253 -0
  182. wappa/webhooks/core/webhook_interfaces/__init__.py +48 -0
  183. wappa/webhooks/core/webhook_interfaces/base_components.py +293 -0
  184. wappa/webhooks/core/webhook_interfaces/universal_webhooks.py +441 -0
  185. wappa/webhooks/factory.py +754 -0
  186. wappa/webhooks/whatsapp/__init__.py +6 -0
  187. wappa/webhooks/whatsapp/base_models.py +285 -0
  188. wappa/webhooks/whatsapp/message_types/__init__.py +93 -0
  189. wappa/webhooks/whatsapp/message_types/audio.py +350 -0
  190. wappa/webhooks/whatsapp/message_types/button.py +267 -0
  191. wappa/webhooks/whatsapp/message_types/contact.py +464 -0
  192. wappa/webhooks/whatsapp/message_types/document.py +421 -0
  193. wappa/webhooks/whatsapp/message_types/errors.py +195 -0
  194. wappa/webhooks/whatsapp/message_types/image.py +424 -0
  195. wappa/webhooks/whatsapp/message_types/interactive.py +430 -0
  196. wappa/webhooks/whatsapp/message_types/location.py +416 -0
  197. wappa/webhooks/whatsapp/message_types/order.py +372 -0
  198. wappa/webhooks/whatsapp/message_types/reaction.py +271 -0
  199. wappa/webhooks/whatsapp/message_types/sticker.py +328 -0
  200. wappa/webhooks/whatsapp/message_types/system.py +317 -0
  201. wappa/webhooks/whatsapp/message_types/text.py +411 -0
  202. wappa/webhooks/whatsapp/message_types/unsupported.py +273 -0
  203. wappa/webhooks/whatsapp/message_types/video.py +344 -0
  204. wappa/webhooks/whatsapp/status_models.py +479 -0
  205. wappa/webhooks/whatsapp/validators.py +454 -0
  206. wappa/webhooks/whatsapp/webhook_container.py +438 -0
  207. wappa-0.1.0.dist-info/METADATA +269 -0
  208. wappa-0.1.0.dist-info/RECORD +211 -0
  209. wappa-0.1.0.dist-info/WHEEL +4 -0
  210. wappa-0.1.0.dist-info/entry_points.txt +2 -0
  211. wappa-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,108 @@
1
+ """
2
+ Repository factory interface.
3
+
4
+ Defines contract for creating context-aware repository instances.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+
9
+ from .expiry_repository import IExpiryRepository
10
+ from .pubsub_repository import IPubSubRepository
11
+ from .shared_state_repository import ISharedStateRepository
12
+ from .state_repository import IStateRepository
13
+ from .tables_repository import ITablesRepository
14
+ from .user_repository import IUserRepository
15
+
16
+
17
+ class IRepositoryFactory(ABC):
18
+ """
19
+ Interface for creating context-aware repository instances.
20
+
21
+ Ensures all repositories are bound to the correct tenant and user context.
22
+ """
23
+
24
+ @abstractmethod
25
+ def create_user_repository(self, tenant_id: str, user_id: str) -> IUserRepository:
26
+ """
27
+ Create user repository with context binding.
28
+
29
+ Args:
30
+ tenant_id: Tenant identifier
31
+ user_id: User identifier
32
+
33
+ Returns:
34
+ Context-bound user repository instance
35
+ """
36
+ pass
37
+
38
+ @abstractmethod
39
+ def create_state_repository(self, tenant_id: str, user_id: str) -> IStateRepository:
40
+ """
41
+ Create state repository with context binding.
42
+
43
+ Args:
44
+ tenant_id: Tenant identifier
45
+ user_id: User identifier
46
+
47
+ Returns:
48
+ Context-bound state repository instance
49
+ """
50
+ pass
51
+
52
+ @abstractmethod
53
+ def create_shared_state_repository(
54
+ self, tenant_id: str, user_id: str
55
+ ) -> ISharedStateRepository:
56
+ """
57
+ Create shared state repository with context binding.
58
+
59
+ Args:
60
+ tenant_id: Tenant identifier
61
+ user_id: User identifier
62
+
63
+ Returns:
64
+ Context-bound shared state repository instance
65
+ """
66
+ pass
67
+
68
+ @abstractmethod
69
+ def create_expiry_repository(self, tenant_id: str) -> IExpiryRepository:
70
+ """
71
+ Create expiry repository with context binding.
72
+
73
+ Args:
74
+ tenant_id: Tenant identifier
75
+
76
+ Returns:
77
+ Context-bound expiry repository instance
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ def create_pubsub_repository(
83
+ self, tenant_id: str, user_id: str | None = None
84
+ ) -> IPubSubRepository:
85
+ """
86
+ Create pub/sub repository with context binding.
87
+
88
+ Args:
89
+ tenant_id: Tenant identifier
90
+ user_id: Optional user identifier (for user-specific channels)
91
+
92
+ Returns:
93
+ Context-bound pub/sub repository instance
94
+ """
95
+ pass
96
+
97
+ @abstractmethod
98
+ def create_tables_repository(self, tenant_id: str) -> ITablesRepository:
99
+ """
100
+ Create tables repository with context binding.
101
+
102
+ Args:
103
+ tenant_id: Tenant identifier
104
+
105
+ Returns:
106
+ Context-bound tables repository instance
107
+ """
108
+ pass
@@ -0,0 +1,122 @@
1
+ """
2
+ Shared state repository interface.
3
+
4
+ Defines contract for shared state management in Redis.
5
+ """
6
+
7
+ from abc import abstractmethod
8
+ from datetime import timedelta
9
+ from typing import Any
10
+
11
+ from pydantic import BaseModel
12
+
13
+ from .base_repository import IBaseRepository
14
+
15
+
16
+ class ISharedStateRepository(IBaseRepository):
17
+ """
18
+ Interface for shared state management.
19
+
20
+ Handles shared state and tool scratch-space with context binding.
21
+ Uses the 'symphony_shared_state' Redis pool (database 3).
22
+ """
23
+
24
+ @abstractmethod
25
+ async def get_shared_state(
26
+ self,
27
+ state_name: str,
28
+ models: type[BaseModel] | None = None,
29
+ ) -> dict[str, Any] | None:
30
+ """
31
+ Get shared state for user with optional BaseModel deserialization.
32
+
33
+ Args:
34
+ state_name: Name of the state (e.g., 'conversation', 'context')
35
+ models: Optional BaseModel class for full object reconstruction
36
+
37
+ Returns:
38
+ Shared state dictionary or None if not found
39
+ """
40
+ pass
41
+
42
+ @abstractmethod
43
+ async def set_shared_state(
44
+ self,
45
+ state_name: str,
46
+ state_data: dict[str, Any],
47
+ ttl: timedelta | None = None,
48
+ ) -> bool:
49
+ """
50
+ Set shared state for user.
51
+
52
+ Args:
53
+ state_name: Name of the state
54
+ state_data: State data dictionary
55
+ ttl: Optional time-to-live
56
+
57
+ Returns:
58
+ True if successful
59
+ """
60
+ pass
61
+
62
+ @abstractmethod
63
+ async def get_state_field(self, state_name: str, field: str) -> Any | None:
64
+ """
65
+ Get specific field from shared state.
66
+
67
+ Args:
68
+ state_name: Name of the state
69
+ field: Field name
70
+
71
+ Returns:
72
+ Field value or None if not found
73
+ """
74
+ pass
75
+
76
+ @abstractmethod
77
+ async def set_state_field(self, state_name: str, field: str, value: Any) -> bool:
78
+ """
79
+ Set specific field in shared state.
80
+
81
+ Args:
82
+ state_name: Name of the state
83
+ field: Field name
84
+ value: Field value
85
+
86
+ Returns:
87
+ True if successful
88
+ """
89
+ pass
90
+
91
+ @abstractmethod
92
+ async def delete_shared_state(self, state_name: str) -> bool:
93
+ """
94
+ Delete complete shared state.
95
+
96
+ Args:
97
+ state_name: Name of the state
98
+
99
+ Returns:
100
+ True if state was deleted
101
+ """
102
+ pass
103
+
104
+ @abstractmethod
105
+ async def get_user_states(self) -> list[str]:
106
+ """
107
+ Get all active state names for user.
108
+
109
+ Returns:
110
+ List of state names
111
+ """
112
+ pass
113
+
114
+ @abstractmethod
115
+ async def clear_user_states(self) -> bool:
116
+ """
117
+ Clear all shared states for user.
118
+
119
+ Returns:
120
+ True if states were cleared
121
+ """
122
+ pass
@@ -0,0 +1,123 @@
1
+ """
2
+ State repository interface.
3
+
4
+ Defines contract for handler state management in Redis.
5
+ """
6
+
7
+ from abc import abstractmethod
8
+ from datetime import timedelta
9
+ from typing import Any
10
+
11
+ from pydantic import BaseModel
12
+
13
+ from .base_repository import IBaseRepository
14
+
15
+
16
+ class IStateRepository(IBaseRepository):
17
+ """
18
+ Interface for handler state management.
19
+
20
+ Handles flow states and handler-specific data with context binding.
21
+ Uses the 'handlers' Redis pool (database 2).
22
+ """
23
+
24
+ @abstractmethod
25
+ async def get_handler_state(
26
+ self,
27
+ handler_type: str,
28
+ models: type[BaseModel] | None = None,
29
+ ) -> dict[str, Any] | None:
30
+ """
31
+ Get handler state for user with optional BaseModel deserialization.
32
+
33
+ Args:
34
+ handler_type: Type of handler (e.g., 'registration', 'reservation')
35
+ models: Optional BaseModel class for full object reconstruction
36
+ e.g., DemoHotelRegistrationState (will automatically handle nested models)
37
+
38
+ Returns:
39
+ Handler state dictionary or None if not found
40
+ """
41
+ pass
42
+
43
+ @abstractmethod
44
+ async def set_handler_state(
45
+ self,
46
+ handler_type: str,
47
+ state_data: dict[str, Any],
48
+ ttl: timedelta | None = None,
49
+ ) -> bool:
50
+ """
51
+ Set handler state for user.
52
+
53
+ Args:
54
+ handler_type: Type of handler
55
+ state_data: State data dictionary
56
+ ttl: Optional time-to-live
57
+
58
+ Returns:
59
+ True if successful
60
+ """
61
+ pass
62
+
63
+ @abstractmethod
64
+ async def get_state_field(self, handler_type: str, field: str) -> Any | None:
65
+ """
66
+ Get specific field from handler state.
67
+
68
+ Args:
69
+ handler_type: Type of handler
70
+ field: Field name
71
+
72
+ Returns:
73
+ Field value or None if not found
74
+ """
75
+ pass
76
+
77
+ @abstractmethod
78
+ async def set_state_field(self, handler_type: str, field: str, value: Any) -> bool:
79
+ """
80
+ Set specific field in handler state.
81
+
82
+ Args:
83
+ handler_type: Type of handler
84
+ field: Field name
85
+ value: Field value
86
+
87
+ Returns:
88
+ True if successful
89
+ """
90
+ pass
91
+
92
+ @abstractmethod
93
+ async def delete_handler_state(self, handler_type: str) -> bool:
94
+ """
95
+ Delete complete handler state.
96
+
97
+ Args:
98
+ handler_type: Type of handler
99
+
100
+ Returns:
101
+ True if state was deleted
102
+ """
103
+ pass
104
+
105
+ @abstractmethod
106
+ async def get_user_handlers(self) -> list[str]:
107
+ """
108
+ Get all active handler types for user.
109
+
110
+ Returns:
111
+ List of handler type names
112
+ """
113
+ pass
114
+
115
+ @abstractmethod
116
+ async def clear_user_states(self) -> bool:
117
+ """
118
+ Clear all handler states for user.
119
+
120
+ Returns:
121
+ True if states were cleared
122
+ """
123
+ pass
@@ -0,0 +1,215 @@
1
+ """
2
+ Tables repository interface.
3
+
4
+ Defines contract for table data operations (generic DataFrames/rows).
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any
9
+
10
+ from pydantic import BaseModel
11
+
12
+ from .base_repository import IBaseRepository
13
+
14
+
15
+ class ITablesRepository(IBaseRepository, ABC):
16
+ """
17
+ Interface for table data repository operations.
18
+
19
+ Provides contract for generic table/DataFrame data management,
20
+ following the existing RedisTable functionality patterns.
21
+ """
22
+
23
+ @abstractmethod
24
+ async def get_table_data(
25
+ self,
26
+ table_name: str,
27
+ pkid: str,
28
+ models: type[BaseModel] | None = None,
29
+ ) -> dict[str, Any] | None:
30
+ """
31
+ Get full table row data.
32
+
33
+ Args:
34
+ table_name: Name of the table
35
+ pkid: Primary key identifier
36
+ models: Optional BaseModel class for full object reconstruction
37
+
38
+ Returns:
39
+ Table row data dictionary or None if not found
40
+ """
41
+ pass
42
+
43
+ @abstractmethod
44
+ async def set_table_data(
45
+ self,
46
+ table_name: str,
47
+ pkid: str,
48
+ data: dict[str, Any],
49
+ ttl: int | None = None,
50
+ ) -> bool:
51
+ """
52
+ Set table row data (upsert behavior).
53
+
54
+ Args:
55
+ table_name: Name of the table
56
+ pkid: Primary key identifier
57
+ data: Data to store
58
+ ttl: Optional TTL in seconds
59
+
60
+ Returns:
61
+ True if stored successfully
62
+ """
63
+ pass
64
+
65
+ @abstractmethod
66
+ async def get_table_field(
67
+ self, table_name: str, pkid: str, field: str
68
+ ) -> Any | None:
69
+ """
70
+ Get a specific field from table row data.
71
+
72
+ Args:
73
+ table_name: Name of the table
74
+ pkid: Primary key identifier
75
+ field: Field name to retrieve
76
+
77
+ Returns:
78
+ Field value or None if not found
79
+ """
80
+ pass
81
+
82
+ @abstractmethod
83
+ async def update_table_field(
84
+ self,
85
+ table_name: str,
86
+ pkid: str,
87
+ field: str,
88
+ value: Any,
89
+ ttl: int | None = None,
90
+ ) -> bool:
91
+ """
92
+ Update single field in table row.
93
+
94
+ Args:
95
+ table_name: Name of the table
96
+ pkid: Primary key identifier
97
+ field: Field name to update
98
+ value: New field value
99
+ ttl: Optional TTL in seconds
100
+
101
+ Returns:
102
+ True if updated successfully
103
+ """
104
+ pass
105
+
106
+ @abstractmethod
107
+ async def increment_table_field(
108
+ self,
109
+ table_name: str,
110
+ pkid: str,
111
+ field: str,
112
+ increment: int = 1,
113
+ ttl: int | None = None,
114
+ ) -> int | None:
115
+ """
116
+ Atomically increment integer field.
117
+
118
+ Args:
119
+ table_name: Name of the table
120
+ pkid: Primary key identifier
121
+ field: Field name to increment
122
+ increment: Amount to increment by (default: 1)
123
+ ttl: Optional TTL in seconds
124
+
125
+ Returns:
126
+ New field value after increment, or None if failed
127
+ """
128
+ pass
129
+
130
+ @abstractmethod
131
+ async def append_to_table_list_field(
132
+ self,
133
+ table_name: str,
134
+ pkid: str,
135
+ field: str,
136
+ value: Any,
137
+ ttl: int | None = None,
138
+ ) -> bool:
139
+ """
140
+ Append value to list field.
141
+
142
+ Args:
143
+ table_name: Name of the table
144
+ pkid: Primary key identifier
145
+ field: Field name containing the list
146
+ value: Value to append
147
+ ttl: Optional TTL in seconds
148
+
149
+ Returns:
150
+ True if appended successfully
151
+ """
152
+ pass
153
+
154
+ @abstractmethod
155
+ async def table_data_exists(self, table_name: str, pkid: str) -> bool:
156
+ """
157
+ Check if table row exists.
158
+
159
+ Args:
160
+ table_name: Name of the table
161
+ pkid: Primary key identifier
162
+
163
+ Returns:
164
+ True if table row exists
165
+ """
166
+ pass
167
+
168
+ @abstractmethod
169
+ async def delete_table_data(self, table_name: str, pkid: str) -> int:
170
+ """
171
+ Delete table row.
172
+
173
+ Args:
174
+ table_name: Name of the table
175
+ pkid: Primary key identifier
176
+
177
+ Returns:
178
+ Number of keys deleted
179
+ """
180
+ pass
181
+
182
+ @abstractmethod
183
+ async def find_table_by_field(
184
+ self,
185
+ table_name: str,
186
+ field: str,
187
+ value: Any,
188
+ models: type[BaseModel] | None = None,
189
+ ) -> dict[str, Any] | None:
190
+ """
191
+ Find first row in table where field matches value.
192
+
193
+ Args:
194
+ table_name: Name of the table
195
+ field: Field name to search
196
+ value: Value to match
197
+ models: Optional BaseModel class for full object reconstruction
198
+
199
+ Returns:
200
+ First matching row data or None if not found
201
+ """
202
+ pass
203
+
204
+ @abstractmethod
205
+ async def delete_all_tables_by_pkid(self, pkid: str) -> int:
206
+ """
207
+ Delete all table rows across all tables with same pkid.
208
+
209
+ Args:
210
+ pkid: Primary key identifier
211
+
212
+ Returns:
213
+ Number of keys deleted
214
+ """
215
+ pass
@@ -0,0 +1,114 @@
1
+ """
2
+ User repository interface.
3
+
4
+ Defines contract for user state management in Redis.
5
+ """
6
+
7
+ from abc import abstractmethod
8
+ from datetime import timedelta
9
+ from typing import Any
10
+
11
+ from pydantic import BaseModel
12
+
13
+ from .base_repository import IBaseRepository
14
+
15
+
16
+ class IUserRepository(IBaseRepository):
17
+ """
18
+ Interface for user state management.
19
+
20
+ Handles user-specific data with context binding (tenant_id + user_id).
21
+ Uses the 'user' Redis pool (database 1).
22
+ """
23
+
24
+ @abstractmethod
25
+ async def get_user_data(
26
+ self, models: type[BaseModel] | None = None
27
+ ) -> dict[str, Any] | None:
28
+ """
29
+ Get complete user data hash with optional BaseModel deserialization.
30
+
31
+ Args:
32
+ models: Optional BaseModel class for full object reconstruction
33
+ e.g., User (will automatically handle nested UserContact, UserLocation)
34
+
35
+ Returns:
36
+ User data dictionary or None if not found
37
+ """
38
+ pass
39
+
40
+ @abstractmethod
41
+ async def set_user_data(
42
+ self, data: dict[str, Any], ttl: timedelta | None = None
43
+ ) -> bool:
44
+ """
45
+ Set complete user data hash.
46
+
47
+ Args:
48
+ data: User data dictionary
49
+ ttl: Optional time-to-live
50
+
51
+ Returns:
52
+ True if successful
53
+ """
54
+ pass
55
+
56
+ @abstractmethod
57
+ async def get_user_field(self, field: str) -> Any | None:
58
+ """
59
+ Get specific field from user data.
60
+
61
+ Args:
62
+ field: Field name
63
+
64
+ Returns:
65
+ Field value or None if not found
66
+ """
67
+ pass
68
+
69
+ @abstractmethod
70
+ async def set_user_field(self, field: str, value: Any) -> bool:
71
+ """
72
+ Set specific field in user data.
73
+
74
+ Args:
75
+ field: Field name
76
+ value: Field value
77
+
78
+ Returns:
79
+ True if successful
80
+ """
81
+ pass
82
+
83
+ @abstractmethod
84
+ async def delete_user_field(self, field: str) -> bool:
85
+ """
86
+ Delete specific field from user data.
87
+
88
+ Args:
89
+ field: Field name
90
+
91
+ Returns:
92
+ True if field was deleted
93
+ """
94
+ pass
95
+
96
+ @abstractmethod
97
+ async def user_exists(self) -> bool:
98
+ """
99
+ Check if user data exists.
100
+
101
+ Returns:
102
+ True if user data exists
103
+ """
104
+ pass
105
+
106
+ @abstractmethod
107
+ async def delete_user(self) -> bool:
108
+ """
109
+ Delete all user data.
110
+
111
+ Returns:
112
+ True if user was deleted
113
+ """
114
+ pass
@@ -0,0 +1 @@
1
+ """Webhook interface definitions."""