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,183 @@
1
+ """
2
+ Rate Limit Plugin
3
+
4
+ Plugin for adding rate limiting middleware to Wappa applications.
5
+ Provides protection against abuse and DoS attacks.
6
+ """
7
+
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from ...core.logging.logger import get_app_logger
11
+
12
+ if TYPE_CHECKING:
13
+ from fastapi import FastAPI
14
+
15
+ from ...core.factory.wappa_builder import WappaBuilder
16
+
17
+
18
+ class RateLimitPlugin:
19
+ """
20
+ Rate limiting middleware plugin for Wappa applications.
21
+
22
+ Provides request rate limiting to protect against abuse and DoS attacks.
23
+ Can be configured with different strategies and backends.
24
+
25
+ Example:
26
+ # Basic rate limiting
27
+ rate_limit_plugin = RateLimitPlugin(
28
+ RateLimiterMiddleware,
29
+ max_requests=100,
30
+ window_seconds=60
31
+ )
32
+
33
+ # Redis-backed rate limiting
34
+ rate_limit_plugin = RateLimitPlugin(
35
+ RedisRateLimiterMiddleware,
36
+ max_requests=1000,
37
+ window_seconds=3600,
38
+ redis_url="redis://localhost:6379"
39
+ )
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ rate_limit_middleware_class: type,
45
+ priority: int = 70, # Medium-high priority
46
+ **middleware_kwargs: Any,
47
+ ):
48
+ """
49
+ Initialize rate limit plugin.
50
+
51
+ Args:
52
+ rate_limit_middleware_class: Rate limiting middleware class
53
+ priority: Middleware priority (lower runs first/outer)
54
+ **middleware_kwargs: Arguments for the middleware class
55
+ """
56
+ self.rate_limit_middleware_class = rate_limit_middleware_class
57
+ self.priority = priority
58
+ self.middleware_kwargs = middleware_kwargs
59
+
60
+ async def configure(self, builder: "WappaBuilder") -> None:
61
+ """
62
+ Configure rate limit plugin with WappaBuilder.
63
+
64
+ Adds the rate limiting middleware to the application.
65
+
66
+ Args:
67
+ builder: WappaBuilder instance
68
+ """
69
+ logger = get_app_logger()
70
+
71
+ # Add rate limiting middleware to builder
72
+ builder.add_middleware(
73
+ self.rate_limit_middleware_class,
74
+ priority=self.priority,
75
+ **self.middleware_kwargs,
76
+ )
77
+
78
+ logger.debug(
79
+ f"RateLimitPlugin configured with {self.rate_limit_middleware_class.__name__} "
80
+ f"(priority: {self.priority})"
81
+ )
82
+
83
+ async def startup(self, app: "FastAPI") -> None:
84
+ """
85
+ Rate limit plugin startup.
86
+
87
+ Can be used for rate limiter backend initialization.
88
+
89
+ Args:
90
+ app: FastAPI application instance
91
+ """
92
+ logger = get_app_logger()
93
+ logger.debug(
94
+ f"RateLimitPlugin startup - {self.rate_limit_middleware_class.__name__}"
95
+ )
96
+
97
+ async def shutdown(self, app: "FastAPI") -> None:
98
+ """
99
+ Rate limit plugin shutdown.
100
+
101
+ Can be used for cleaning up rate limiter resources.
102
+
103
+ Args:
104
+ app: FastAPI application instance
105
+ """
106
+ logger = get_app_logger()
107
+ logger.debug(
108
+ f"RateLimitPlugin shutdown - {self.rate_limit_middleware_class.__name__}"
109
+ )
110
+
111
+
112
+ # Convenience functions for common rate limiting patterns
113
+
114
+
115
+ def create_memory_rate_limit_plugin(
116
+ max_requests: int = 100, window_seconds: int = 60, **kwargs: Any
117
+ ) -> RateLimitPlugin:
118
+ """
119
+ Create an in-memory rate limiting plugin.
120
+
121
+ Note: This is a convenience function. You'll need to provide
122
+ an actual rate limiting middleware implementation.
123
+
124
+ Args:
125
+ max_requests: Maximum requests per window
126
+ window_seconds: Time window in seconds
127
+ **kwargs: Additional rate limiter arguments
128
+
129
+ Returns:
130
+ Configured RateLimitPlugin for in-memory rate limiting
131
+ """
132
+ try:
133
+ # This is a placeholder - you'd import your actual rate limiting middleware
134
+ from your_rate_limit_library import MemoryRateLimiterMiddleware
135
+
136
+ return RateLimitPlugin(
137
+ MemoryRateLimiterMiddleware,
138
+ max_requests=max_requests,
139
+ window_seconds=window_seconds,
140
+ **kwargs,
141
+ )
142
+ except ImportError:
143
+ raise ImportError(
144
+ "Rate limiting middleware not found. Please implement or install a rate limiting middleware library."
145
+ )
146
+
147
+
148
+ def create_redis_rate_limit_plugin(
149
+ max_requests: int = 1000,
150
+ window_seconds: int = 3600,
151
+ redis_url: str = "redis://localhost:6379",
152
+ **kwargs: Any,
153
+ ) -> RateLimitPlugin:
154
+ """
155
+ Create a Redis-backed rate limiting plugin.
156
+
157
+ Note: This is a convenience function. You'll need to provide
158
+ an actual Redis rate limiting middleware implementation.
159
+
160
+ Args:
161
+ max_requests: Maximum requests per window
162
+ window_seconds: Time window in seconds
163
+ redis_url: Redis connection URL
164
+ **kwargs: Additional rate limiter arguments
165
+
166
+ Returns:
167
+ Configured RateLimitPlugin for Redis-backed rate limiting
168
+ """
169
+ try:
170
+ # This is a placeholder - you'd import your actual Redis rate limiting middleware
171
+ from your_rate_limit_library import RedisRateLimiterMiddleware
172
+
173
+ return RateLimitPlugin(
174
+ RedisRateLimiterMiddleware,
175
+ max_requests=max_requests,
176
+ window_seconds=window_seconds,
177
+ redis_url=redis_url,
178
+ **kwargs,
179
+ )
180
+ except ImportError:
181
+ raise ImportError(
182
+ "Redis rate limiting middleware not found. Please implement or install a Redis rate limiting middleware library."
183
+ )
@@ -0,0 +1,224 @@
1
+ """
2
+ Redis Plugin
3
+
4
+ Plugin for integrating Redis caching functionality with the Wappa framework.
5
+ Uses the Wappa library's own RedisManager for clean dependency management.
6
+ """
7
+
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from ...core.logging.logger import get_app_logger
11
+ from ...persistence.redis.redis_manager import RedisManager
12
+
13
+ if TYPE_CHECKING:
14
+ from fastapi import FastAPI
15
+
16
+ from ...core.factory.wappa_builder import WappaBuilder
17
+
18
+
19
+ class RedisPlugin:
20
+ """
21
+ Redis plugin for Wappa applications.
22
+
23
+ This plugin integrates Redis caching functionality using Wappa's own
24
+ RedisManager and ICacheFactory pattern. It automatically uses settings.redis_url
25
+ and provides clean dependency injection through the event handler cache_factory.
26
+
27
+ Example:
28
+ # Basic usage (uses settings.redis_url automatically)
29
+ redis_plugin = RedisPlugin()
30
+
31
+ # With custom max connections
32
+ redis_plugin = RedisPlugin(max_connections=100)
33
+
34
+ builder.add_plugin(redis_plugin)
35
+
36
+ # Access in event handlers through ICacheFactory
37
+ user_cache = self.cache_factory.create_user_cache(tenant_id, user_id)
38
+ await user_cache.set('key', 'value')
39
+ """
40
+
41
+ def __init__(self, max_connections: int | None = None, **redis_config: Any):
42
+ """
43
+ Initialize Redis plugin.
44
+
45
+ Redis URL is automatically loaded from settings.redis_url.
46
+ The plugin uses hook-based architecture - Redis initialization
47
+ happens during the startup hook, not during plugin construction.
48
+
49
+ Args:
50
+ max_connections: Max Redis connections (defaults to settings.redis_max_connections)
51
+ **redis_config: Additional Redis configuration options
52
+ """
53
+ self.max_connections = max_connections
54
+ self.redis_config = redis_config
55
+
56
+ def configure(self, builder: "WappaBuilder") -> None:
57
+ """
58
+ Configure Redis plugin with WappaBuilder using hook-based architecture.
59
+
60
+ Registers Redis initialization and cleanup as hooks with the builder's
61
+ unified lifespan management system. This ensures Redis starts after
62
+ core Wappa functionality (logging) and shuts down before core cleanup.
63
+
64
+ Args:
65
+ builder: WappaBuilder instance to register hooks with
66
+ """
67
+ # Register Redis lifecycle hooks with appropriate priorities
68
+ # Priority 20: After core startup (10) but before user hooks (50)
69
+ builder.add_startup_hook(self._redis_startup, priority=20)
70
+ builder.add_shutdown_hook(self._redis_shutdown, priority=20)
71
+
72
+ logger = get_app_logger()
73
+ logger.debug("🔧 RedisPlugin configured - registered startup/shutdown hooks")
74
+
75
+ async def startup(self, app: "FastAPI") -> None:
76
+ """
77
+ Plugin startup method required by WappaPlugin protocol.
78
+
79
+ Delegates to _redis_startup hook method for actual implementation.
80
+ This maintains compatibility with both the plugin protocol and
81
+ the hook-based architecture.
82
+
83
+ Args:
84
+ app: FastAPI application instance
85
+ """
86
+ await self._redis_startup(app)
87
+
88
+ async def shutdown(self, app: "FastAPI") -> None:
89
+ """
90
+ Plugin shutdown method required by WappaPlugin protocol.
91
+
92
+ Delegates to _redis_shutdown hook method for actual implementation.
93
+ This maintains compatibility with both the plugin protocol and
94
+ the hook-based architecture.
95
+
96
+ Args:
97
+ app: FastAPI application instance
98
+ """
99
+ await self._redis_shutdown(app)
100
+
101
+ async def _redis_startup(self, app: "FastAPI") -> None:
102
+ """
103
+ Redis initialization hook - runs after core Wappa startup.
104
+
105
+ This hook is registered with priority 20, ensuring it runs after
106
+ core Wappa functionality (priority 10) has initialized logging
107
+ and other essential services.
108
+
109
+ Args:
110
+ app: FastAPI application instance
111
+ """
112
+ logger = get_app_logger()
113
+
114
+ try:
115
+ # Import settings for explicit configuration
116
+ from ...core.config.settings import settings
117
+
118
+ # Use explicit settings - more readable than None
119
+ redis_url = settings.redis_url
120
+ max_conn = self.max_connections or settings.redis_max_connections
121
+
122
+ logger.info("=== REDIS CACHE INITIALIZATION ===")
123
+ logger.info(
124
+ f"🔴 Redis URL: {redis_url} (max_connections: {max_conn})"
125
+ )
126
+
127
+ # Initialize Redis pools with explicit settings
128
+ await RedisManager.initialize(
129
+ redis_url=redis_url, # Explicit, not None - more readable
130
+ max_connections=max_conn,
131
+ )
132
+
133
+ # Store RedisManager reference in app state for cache factory access
134
+ app.state.redis_manager = RedisManager
135
+
136
+ # Get detailed health status for confirmation
137
+ health_status = await RedisManager.get_health_status()
138
+ healthy_pools = [
139
+ alias
140
+ for alias, status in health_status.get("pools", {}).items()
141
+ if status.get("status") == "healthy"
142
+ ]
143
+
144
+ # Success confirmation with pool details
145
+ pool_info = RedisManager.get_pool_info()
146
+ logger.info(
147
+ f"✅ Redis startup completed! "
148
+ f"Healthy pools: {len(healthy_pools)}/{len(pool_info)} "
149
+ f"({', '.join(f'{alias}:db{db}' for alias, db in pool_info.items())})"
150
+ )
151
+ logger.info("===============================")
152
+
153
+ # Debug detailed pool information
154
+ logger.debug(f"Redis pool health details: {health_status}")
155
+
156
+ except Exception as e:
157
+ logger.error(f"❌ Redis startup hook failed: {e}", exc_info=True)
158
+ raise RuntimeError(f"RedisPlugin startup hook failed: {e}") from e
159
+
160
+ async def _redis_shutdown(self, app: "FastAPI") -> None:
161
+ """
162
+ Redis cleanup hook - runs before core Wappa shutdown.
163
+
164
+ This hook is registered with priority 20, ensuring it runs before
165
+ core Wappa cleanup (priority 90) to properly close Redis connections
166
+ while logging is still available.
167
+
168
+ Args:
169
+ app: FastAPI application instance
170
+ """
171
+ logger = get_app_logger()
172
+
173
+ try:
174
+ # Clean up Redis connections if initialized
175
+ if hasattr(app.state, "redis_manager") and RedisManager.is_initialized():
176
+ logger.info("=== REDIS CACHE SHUTDOWN ===")
177
+ logger.debug("🔴 Cleaning up Redis connections...")
178
+ await RedisManager.cleanup()
179
+ logger.info("✅ Redis shutdown completed")
180
+ logger.info("==============================")
181
+
182
+ # Clean up app state
183
+ if hasattr(app.state, "redis_manager"):
184
+ del app.state.redis_manager
185
+ logger.debug("🧹 Redis manager removed from app state")
186
+
187
+ except Exception as e:
188
+ # Don't re-raise in shutdown - log and continue
189
+ logger.error(f"❌ Error during Redis shutdown hook: {e}", exc_info=True)
190
+
191
+ async def get_health_status(self, app: "FastAPI") -> dict[str, Any]:
192
+ """
193
+ Get Redis health status for monitoring.
194
+
195
+ Uses the existing RedisManager.get_health_status() method
196
+ to provide consistent health reporting.
197
+
198
+ Args:
199
+ app: FastAPI application instance
200
+
201
+ Returns:
202
+ Dictionary with Redis health information
203
+ """
204
+ if not RedisManager.is_initialized():
205
+ return {
206
+ "healthy": False,
207
+ "error": "Redis manager not initialized",
208
+ "initialized": False,
209
+ "plugin": "RedisPlugin",
210
+ }
211
+
212
+ try:
213
+ health_status = await RedisManager.get_health_status()
214
+ return {
215
+ "healthy": health_status.get("initialized", False),
216
+ "plugin": "RedisPlugin",
217
+ **health_status,
218
+ }
219
+ except Exception as e:
220
+ return {
221
+ "healthy": False,
222
+ "error": str(e),
223
+ "plugin": "RedisPlugin",
224
+ }
@@ -0,0 +1,261 @@
1
+ """
2
+ Wappa Core Plugin
3
+
4
+ This plugin encapsulates all traditional Wappa functionality, providing the foundation
5
+ for the Wappa framework through the plugin system. It includes logging, middleware,
6
+ routes, and lifespan management that was previously hardcoded in the Wappa class.
7
+ """
8
+
9
+ from typing import TYPE_CHECKING
10
+
11
+ import aiohttp
12
+ from fastapi import FastAPI
13
+
14
+ from wappa.api.middleware.error_handler import ErrorHandlerMiddleware
15
+ from wappa.api.middleware.owner import OwnerMiddleware
16
+ from wappa.api.middleware.request_logging import RequestLoggingMiddleware
17
+ from wappa.api.routes.health import router as health_router
18
+ from wappa.api.routes.whatsapp_combined import whatsapp_router
19
+
20
+ from ..config.settings import settings
21
+ from ..logging.logger import get_app_logger, setup_app_logging
22
+ from ..types import CacheType
23
+
24
+ if TYPE_CHECKING:
25
+ from ..factory.wappa_builder import WappaBuilder
26
+
27
+
28
+ class WappaCorePlugin:
29
+ """
30
+ Core Wappa functionality as a plugin.
31
+
32
+ This plugin provides all the essential Wappa framework functionality:
33
+ - Application logging setup
34
+ - HTTP session management
35
+ - Core middleware stack (Owner, ErrorHandler, RequestLogging)
36
+ - Core routes (Health, WhatsApp combined)
37
+ - Webhook URL generation and logging
38
+ - Cache type configuration
39
+
40
+ By implementing core functionality as a plugin, we achieve a unified
41
+ architecture where all Wappa applications use the same plugin-based
42
+ foundation, whether they use simple Wappa() or advanced WappaBuilder.
43
+ """
44
+
45
+ def __init__(self, cache_type: CacheType = CacheType.MEMORY):
46
+ """
47
+ Initialize Wappa core plugin.
48
+
49
+ Args:
50
+ cache_type: Cache backend to use for the application
51
+ """
52
+ self.cache_type = cache_type
53
+
54
+ def configure(self, builder: "WappaBuilder") -> None:
55
+ """
56
+ Configure core Wappa functionality with the builder.
57
+
58
+ Registers:
59
+ - Core middleware with appropriate priorities
60
+ - Core routes (health, whatsapp)
61
+ - Core startup/shutdown hooks
62
+
63
+ Args:
64
+ builder: WappaBuilder instance to configure
65
+ """
66
+ logger = get_app_logger()
67
+ logger.debug("🏗️ Configuring WappaCorePlugin...")
68
+
69
+ # Register core middleware with proper priorities
70
+ # Higher priority numbers run closer to routes (inner middleware)
71
+ builder.add_middleware(
72
+ OwnerMiddleware, priority=90
73
+ ) # Outer - tenant extraction
74
+ builder.add_middleware(ErrorHandlerMiddleware, priority=80) # Error handling
75
+ builder.add_middleware(
76
+ RequestLoggingMiddleware, priority=70
77
+ ) # Request logging (inner)
78
+
79
+ # Register core routes
80
+ builder.add_router(health_router)
81
+ builder.add_router(whatsapp_router)
82
+
83
+ # Register core lifespan hooks with high priority (runs first/last)
84
+ builder.add_startup_hook(self._core_startup, priority=10) # First to run
85
+ builder.add_shutdown_hook(self._core_shutdown, priority=90) # Last to run
86
+
87
+ logger.debug(
88
+ f"✅ WappaCorePlugin configured - cache_type: {self.cache_type.value}, "
89
+ f"middleware: 3, routes: 2, hooks: 2"
90
+ )
91
+
92
+ async def startup(self, app: FastAPI) -> None:
93
+ """
94
+ Plugin startup method required by WappaPlugin protocol.
95
+
96
+ Delegates to _core_startup hook method for actual implementation.
97
+ This maintains compatibility with both the plugin protocol and
98
+ the hook-based architecture.
99
+
100
+ Args:
101
+ app: FastAPI application instance
102
+ """
103
+ await self._core_startup(app)
104
+
105
+ async def shutdown(self, app: FastAPI) -> None:
106
+ """
107
+ Plugin shutdown method required by WappaPlugin protocol.
108
+
109
+ Delegates to _core_shutdown hook method for actual implementation.
110
+ This maintains compatibility with both the plugin protocol and
111
+ the hook-based architecture.
112
+
113
+ Args:
114
+ app: FastAPI application instance
115
+ """
116
+ await self._core_shutdown(app)
117
+
118
+ async def _core_startup(self, app: FastAPI) -> None:
119
+ """
120
+ Core Wappa startup functionality.
121
+
122
+ This hook runs first (priority 10) to establish the foundation that
123
+ other plugins and hooks can depend on:
124
+ - Logging system initialization
125
+ - HTTP session for connection pooling
126
+ - Cache type configuration
127
+ - Webhook URL generation and display
128
+ - Development mode configuration
129
+
130
+ Args:
131
+ app: FastAPI application instance
132
+ """
133
+ logger = None
134
+ try:
135
+ # Initialize logging first - this is critical for all other operations
136
+ setup_app_logging()
137
+ logger = get_app_logger()
138
+
139
+ logger.info(f"🚀 Starting Wappa Framework v{settings.version}")
140
+ logger.info(f"📊 Environment: {settings.environment}")
141
+ logger.info(f"👤 Owner ID: {settings.owner_id}")
142
+ logger.info(f"📝 Log level: {settings.log_level}")
143
+ logger.info(f"💾 Cache type: {self.cache_type.value}")
144
+
145
+ if settings.is_development:
146
+ logger.info(f"🔧 Development mode - logs: {settings.log_dir}")
147
+
148
+ # Set cache type in app state for WebhookController detection
149
+ app.state.wappa_cache_type = self.cache_type.value
150
+ logger.debug(f"💾 Set app.state.wappa_cache_type = {self.cache_type.value}")
151
+
152
+ # Initialize HTTP session for connection pooling (correct scope for app lifecycle)
153
+ app.state.http_session = aiohttp.ClientSession()
154
+ logger.debug("🌐 HTTP session initialized for connection pooling")
155
+
156
+ # Log available endpoints
157
+ base_url = (
158
+ f"http://localhost:{settings.port}"
159
+ if settings.is_development
160
+ else "https://your-domain.com"
161
+ )
162
+ logger.info("=== AVAILABLE ENDPOINTS ===")
163
+ logger.info(f"🏥 Health Check: {base_url}/health")
164
+ logger.info(f"📊 Detailed Health: {base_url}/health/detailed")
165
+ logger.info(f"📱 WhatsApp API: {base_url}/api/whatsapp/...")
166
+ logger.info(
167
+ f"📖 API Documentation: {base_url}/docs"
168
+ if settings.is_development
169
+ else "📖 API docs disabled in production"
170
+ )
171
+ logger.info("============================")
172
+
173
+ # Generate and display WhatsApp webhook URL
174
+ await self._display_webhook_urls(logger, base_url)
175
+
176
+ logger.info("✅ Wappa core startup completed successfully")
177
+
178
+ except Exception as e:
179
+ if logger:
180
+ logger.error(f"❌ Error during Wappa core startup: {e}", exc_info=True)
181
+ else:
182
+ print(f"💥 Critical error during logging setup: {e}")
183
+ raise
184
+
185
+ async def _core_shutdown(self, app: FastAPI) -> None:
186
+ """
187
+ Core Wappa shutdown functionality.
188
+
189
+ This hook runs last (priority 90) to clean up core resources after
190
+ all other plugins have shut down:
191
+ - Close HTTP session
192
+ - Final logging
193
+
194
+ Args:
195
+ app: FastAPI application instance
196
+ """
197
+ logger = get_app_logger()
198
+ logger.info("🛑 Starting Wappa core shutdown...")
199
+
200
+ try:
201
+ # Close HTTP session if it exists
202
+ if hasattr(app.state, "http_session"):
203
+ await app.state.http_session.close()
204
+ logger.debug("🌐 HTTP session closed")
205
+
206
+ # Clear cache type from app state
207
+ if hasattr(app.state, "wappa_cache_type"):
208
+ del app.state.wappa_cache_type
209
+ logger.debug("💾 Cache type cleared from app state")
210
+
211
+ logger.info("✅ Wappa core shutdown completed")
212
+
213
+ except Exception as e:
214
+ logger.error(f"❌ Error during Wappa core shutdown: {e}", exc_info=True)
215
+
216
+ async def _display_webhook_urls(self, logger, base_url: str) -> None:
217
+ """
218
+ Generate and display WhatsApp webhook URLs for user convenience.
219
+
220
+ Args:
221
+ logger: Logger instance
222
+ base_url: Base URL for the application
223
+ """
224
+ try:
225
+ # Import here to avoid circular imports during startup
226
+ from ..events.webhook_factory import webhook_url_factory
227
+
228
+ whatsapp_webhook_url = webhook_url_factory.generate_whatsapp_webhook_url()
229
+
230
+ logger.info("=== WHATSAPP WEBHOOK URL ===")
231
+ logger.info(f"📍 Primary Webhook URL: {whatsapp_webhook_url}")
232
+ logger.info(" • Use this single URL in WhatsApp Business settings")
233
+ logger.info(" • Handles both verification (GET) and webhooks (POST)")
234
+ logger.info(" • Auto-configured with your WP_PHONE_ID from .env")
235
+ logger.info("=============================")
236
+ logger.info("")
237
+
238
+ except Exception as e:
239
+ logger.warning(f"⚠️ Could not generate webhook URL: {e}")
240
+
241
+ def get_cache_type(self) -> CacheType:
242
+ """
243
+ Get the configured cache type.
244
+
245
+ Returns:
246
+ CacheType enum value
247
+ """
248
+ return self.cache_type
249
+
250
+ def set_cache_type(self, cache_type: CacheType) -> None:
251
+ """
252
+ Update the cache type configuration.
253
+
254
+ Args:
255
+ cache_type: New cache type to use
256
+
257
+ Note:
258
+ This should be called before the application starts, as the
259
+ cache type is set in app state during startup.
260
+ """
261
+ self.cache_type = cache_type