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,128 @@
1
+ """
2
+ CORS Plugin
3
+
4
+ Plugin for adding Cross-Origin Resource Sharing (CORS) middleware to Wappa applications.
5
+ Provides a simple wrapper around FastAPI's CORSMiddleware with sensible defaults.
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 CORSPlugin:
19
+ """
20
+ CORS middleware plugin for Wappa applications.
21
+
22
+ Provides Cross-Origin Resource Sharing support with configurable
23
+ origins, methods, and headers. Uses FastAPI's built-in CORSMiddleware
24
+ with sensible defaults for most use cases.
25
+
26
+ Example:
27
+ # Basic CORS (allow all origins)
28
+ cors_plugin = CORSPlugin(allow_origins=["*"])
29
+
30
+ # Production CORS (specific origins)
31
+ cors_plugin = CORSPlugin(
32
+ allow_origins=["https://myapp.com", "https://www.myapp.com"],
33
+ allow_credentials=True,
34
+ allow_methods=["GET", "POST", "PUT", "DELETE"],
35
+ allow_headers=["*"]
36
+ )
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ allow_origins: list[str] = None,
42
+ allow_methods: list[str] = None,
43
+ allow_headers: list[str] = None,
44
+ allow_credentials: bool = False,
45
+ expose_headers: list[str] = None,
46
+ max_age: int = 600,
47
+ priority: int = 90, # High priority - runs early (outer middleware)
48
+ **cors_kwargs: Any,
49
+ ):
50
+ """
51
+ Initialize CORS plugin.
52
+
53
+ Args:
54
+ allow_origins: List of allowed origins (defaults to ["*"])
55
+ allow_methods: List of allowed HTTP methods (defaults to ["GET"])
56
+ allow_headers: List of allowed headers (defaults to [])
57
+ allow_credentials: Whether to allow credentials
58
+ expose_headers: List of headers to expose to the browser
59
+ max_age: Maximum age for preflight requests
60
+ priority: Middleware priority (lower runs first/outer)
61
+ **cors_kwargs: Additional CORSMiddleware arguments
62
+ """
63
+ self.allow_origins = allow_origins or ["*"]
64
+ self.allow_methods = allow_methods or ["GET"]
65
+ self.allow_headers = allow_headers or []
66
+ self.allow_credentials = allow_credentials
67
+ self.expose_headers = expose_headers or []
68
+ self.max_age = max_age
69
+ self.priority = priority
70
+ self.cors_kwargs = cors_kwargs
71
+
72
+ def configure(self, builder: "WappaBuilder") -> None:
73
+ """
74
+ Configure CORS plugin with WappaBuilder.
75
+
76
+ Adds CORSMiddleware to the application with specified configuration.
77
+
78
+ Args:
79
+ builder: WappaBuilder instance
80
+ """
81
+ logger = get_app_logger()
82
+
83
+ try:
84
+ from fastapi.middleware.cors import CORSMiddleware
85
+ except ImportError:
86
+ logger.error(
87
+ "CORSMiddleware not available - ensure FastAPI is properly installed"
88
+ )
89
+ raise RuntimeError("CORSMiddleware not available")
90
+
91
+ # Build CORS configuration
92
+ cors_config = {
93
+ "allow_origins": self.allow_origins,
94
+ "allow_methods": self.allow_methods,
95
+ "allow_headers": self.allow_headers,
96
+ "allow_credentials": self.allow_credentials,
97
+ "expose_headers": self.expose_headers,
98
+ "max_age": self.max_age,
99
+ **self.cors_kwargs,
100
+ }
101
+
102
+ # Add middleware to builder with specified priority
103
+ builder.add_middleware(CORSMiddleware, priority=self.priority, **cors_config)
104
+
105
+ logger.debug(
106
+ f"CORSPlugin configured - Origins: {self.allow_origins}, "
107
+ f"Methods: {self.allow_methods}, Credentials: {self.allow_credentials}"
108
+ )
109
+
110
+ async def startup(self, app: "FastAPI") -> None:
111
+ """
112
+ CORS plugin startup - no startup tasks needed.
113
+
114
+ Args:
115
+ app: FastAPI application instance
116
+ """
117
+ logger = get_app_logger()
118
+ logger.debug("CORSPlugin startup completed")
119
+
120
+ async def shutdown(self, app: "FastAPI") -> None:
121
+ """
122
+ CORS plugin shutdown - no cleanup tasks needed.
123
+
124
+ Args:
125
+ app: FastAPI application instance
126
+ """
127
+ logger = get_app_logger()
128
+ logger.debug("CORSPlugin shutdown completed")
@@ -0,0 +1,182 @@
1
+ """
2
+ Custom Middleware Plugin
3
+
4
+ Plugin for adding user-defined middleware to Wappa applications.
5
+ Provides a flexible wrapper for any custom middleware implementation.
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 CustomMiddlewarePlugin:
19
+ """
20
+ Custom middleware plugin for Wappa applications.
21
+
22
+ Provides a flexible wrapper for any user-defined middleware,
23
+ allowing complete control over middleware configuration and behavior.
24
+
25
+ Example:
26
+ # Request logging middleware
27
+ logging_plugin = CustomMiddlewarePlugin(
28
+ RequestLoggingMiddleware,
29
+ priority=60,
30
+ log_level="INFO"
31
+ )
32
+
33
+ # Security headers middleware
34
+ security_plugin = CustomMiddlewarePlugin(
35
+ SecurityHeadersMiddleware,
36
+ priority=85,
37
+ include_hsts=True,
38
+ include_csp=True
39
+ )
40
+
41
+ # Performance monitoring middleware
42
+ monitoring_plugin = CustomMiddlewarePlugin(
43
+ PerformanceMonitoringMiddleware,
44
+ priority=10, # Low priority - runs last (inner)
45
+ track_response_time=True
46
+ )
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ middleware_class: type,
52
+ priority: int = 50, # Default priority
53
+ name: str = None,
54
+ **middleware_kwargs: Any,
55
+ ):
56
+ """
57
+ Initialize custom middleware plugin.
58
+
59
+ Args:
60
+ middleware_class: Custom middleware class
61
+ priority: Middleware priority (lower runs first/outer)
62
+ name: Optional name for the middleware (for logging)
63
+ **middleware_kwargs: Arguments for the middleware class
64
+ """
65
+ self.middleware_class = middleware_class
66
+ self.priority = priority
67
+ self.name = name or middleware_class.__name__
68
+ self.middleware_kwargs = middleware_kwargs
69
+
70
+ async def configure(self, builder: "WappaBuilder") -> None:
71
+ """
72
+ Configure custom middleware plugin with WappaBuilder.
73
+
74
+ Adds the custom middleware to the application.
75
+
76
+ Args:
77
+ builder: WappaBuilder instance
78
+ """
79
+ logger = get_app_logger()
80
+
81
+ # Add custom middleware to builder
82
+ builder.add_middleware(
83
+ self.middleware_class, priority=self.priority, **self.middleware_kwargs
84
+ )
85
+
86
+ logger.debug(
87
+ f"CustomMiddlewarePlugin configured - {self.name} "
88
+ f"(priority: {self.priority})"
89
+ )
90
+
91
+ async def startup(self, app: "FastAPI") -> None:
92
+ """
93
+ Custom middleware plugin startup.
94
+
95
+ Can be used for middleware-specific initialization tasks.
96
+
97
+ Args:
98
+ app: FastAPI application instance
99
+ """
100
+ logger = get_app_logger()
101
+ logger.debug(f"CustomMiddlewarePlugin startup - {self.name}")
102
+
103
+ async def shutdown(self, app: "FastAPI") -> None:
104
+ """
105
+ Custom middleware plugin shutdown.
106
+
107
+ Can be used for middleware-specific cleanup tasks.
108
+
109
+ Args:
110
+ app: FastAPI application instance
111
+ """
112
+ logger = get_app_logger()
113
+ logger.debug(f"CustomMiddlewarePlugin shutdown - {self.name}")
114
+
115
+
116
+ # Convenience functions for common custom middleware patterns
117
+
118
+
119
+ def create_logging_middleware_plugin(
120
+ middleware_class: type, log_level: str = "INFO", priority: int = 60, **kwargs: Any
121
+ ) -> CustomMiddlewarePlugin:
122
+ """
123
+ Create a logging middleware plugin.
124
+
125
+ Args:
126
+ middleware_class: Logging middleware class
127
+ log_level: Logging level
128
+ priority: Middleware priority
129
+ **kwargs: Additional middleware arguments
130
+
131
+ Returns:
132
+ Configured CustomMiddlewarePlugin for logging
133
+ """
134
+ return CustomMiddlewarePlugin(
135
+ middleware_class,
136
+ priority=priority,
137
+ name="LoggingMiddleware",
138
+ log_level=log_level,
139
+ **kwargs,
140
+ )
141
+
142
+
143
+ def create_security_middleware_plugin(
144
+ middleware_class: type,
145
+ priority: int = 85, # High priority - runs early
146
+ **kwargs: Any,
147
+ ) -> CustomMiddlewarePlugin:
148
+ """
149
+ Create a security middleware plugin.
150
+
151
+ Args:
152
+ middleware_class: Security middleware class
153
+ priority: Middleware priority
154
+ **kwargs: Additional middleware arguments
155
+
156
+ Returns:
157
+ Configured CustomMiddlewarePlugin for security
158
+ """
159
+ return CustomMiddlewarePlugin(
160
+ middleware_class, priority=priority, name="SecurityMiddleware", **kwargs
161
+ )
162
+
163
+
164
+ def create_monitoring_middleware_plugin(
165
+ middleware_class: type,
166
+ priority: int = 10, # Low priority - runs last (inner)
167
+ **kwargs: Any,
168
+ ) -> CustomMiddlewarePlugin:
169
+ """
170
+ Create a monitoring/metrics middleware plugin.
171
+
172
+ Args:
173
+ middleware_class: Monitoring middleware class
174
+ priority: Middleware priority
175
+ **kwargs: Additional middleware arguments
176
+
177
+ Returns:
178
+ Configured CustomMiddlewarePlugin for monitoring
179
+ """
180
+ return CustomMiddlewarePlugin(
181
+ middleware_class, priority=priority, name="MonitoringMiddleware", **kwargs
182
+ )
@@ -0,0 +1,235 @@
1
+ """
2
+ Database Plugin
3
+
4
+ Core plugin for integrating SQLModel/SQLAlchemy database functionality
5
+ with the Wappa framework. Supports multiple database engines through
6
+ adapter pattern.
7
+ """
8
+
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from ...core.logging.logger import get_app_logger
12
+
13
+ if TYPE_CHECKING:
14
+ from fastapi import FastAPI
15
+ from sqlmodel import SQLModel
16
+
17
+ from ...core.factory.wappa_builder import WappaBuilder
18
+ from ...database.adapter import DatabaseAdapter
19
+
20
+
21
+ class DatabasePlugin:
22
+ """
23
+ Universal database plugin for SQLModel/SQLAlchemy integration.
24
+
25
+ This plugin provides database connectivity, session management, and
26
+ schema initialization for any supported database engine (PostgreSQL,
27
+ SQLite, MySQL) through the adapter pattern.
28
+
29
+ Example:
30
+ # PostgreSQL
31
+ db_plugin = DatabasePlugin(
32
+ "postgresql+asyncpg://user:pass@localhost/db",
33
+ PostgreSQLAdapter(),
34
+ models=[User, Order]
35
+ )
36
+
37
+ # SQLite
38
+ db_plugin = DatabasePlugin(
39
+ "sqlite+aiosqlite:///./app.db",
40
+ SQLiteAdapter(),
41
+ models=[User, Order]
42
+ )
43
+
44
+ # Usage in app
45
+ async with app.state.db_session() as session:
46
+ users = await session.exec(select(User))
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ connection_string: str,
52
+ adapter: "DatabaseAdapter",
53
+ models: list[type["SQLModel"]] = None,
54
+ initialize_schema: bool = True,
55
+ **adapter_kwargs: Any,
56
+ ):
57
+ """
58
+ Initialize database plugin.
59
+
60
+ Args:
61
+ connection_string: Database connection URL
62
+ adapter: DatabaseAdapter implementation (PostgreSQL, SQLite, MySQL)
63
+ models: List of SQLModel classes to create tables for
64
+ initialize_schema: Whether to create tables on startup
65
+ **adapter_kwargs: Additional arguments for database adapter
66
+ """
67
+ self.connection_string = connection_string
68
+ self.adapter = adapter
69
+ self.models = models or []
70
+ self.initialize_schema = initialize_schema
71
+ self.adapter_kwargs = adapter_kwargs
72
+
73
+ self.engine = None
74
+ self.session_factory = None
75
+
76
+ async def configure(self, builder: "WappaBuilder") -> None:
77
+ """
78
+ Configure the database plugin with WappaBuilder.
79
+
80
+ This method is called during build phase - no configuration needed
81
+ for database plugin as it manages its own lifecycle.
82
+
83
+ Args:
84
+ builder: WappaBuilder instance
85
+ """
86
+ # Database plugin doesn't need to configure middleware/routes
87
+ pass
88
+
89
+ async def startup(self, app: "FastAPI") -> None:
90
+ """
91
+ Initialize database during application startup.
92
+
93
+ Creates the async engine, session factory, and optionally
94
+ initializes the database schema from SQLModel definitions.
95
+
96
+ Args:
97
+ app: FastAPI application instance
98
+ """
99
+ logger = get_app_logger()
100
+
101
+ try:
102
+ # Create async engine
103
+ logger.debug(
104
+ f"Creating database engine with adapter: {self.adapter.__class__.__name__}"
105
+ )
106
+ self.engine = await self.adapter.create_engine(
107
+ self.connection_string, **self.adapter_kwargs
108
+ )
109
+
110
+ # Create session factory
111
+ logger.debug("Creating database session factory...")
112
+ self.session_factory = await self.adapter.create_session_factory(
113
+ self.engine
114
+ )
115
+
116
+ # Perform health check
117
+ is_healthy = await self.adapter.health_check(self.engine)
118
+ if not is_healthy:
119
+ raise RuntimeError("Database health check failed")
120
+
121
+ # Initialize schema if requested and models provided
122
+ if self.initialize_schema and self.models:
123
+ logger.debug(f"Initializing schema for {len(self.models)} models...")
124
+ await self.adapter.initialize_schema(self.engine, self.models)
125
+ logger.info(
126
+ f"Database schema initialized for models: {[m.__name__ for m in self.models]}"
127
+ )
128
+
129
+ # Store in app state for user access
130
+ app.state.db_engine = self.engine
131
+ app.state.db_session = self.session_factory
132
+ app.state.db_adapter = self.adapter
133
+
134
+ # Get connection info for logging
135
+ connection_info = await self.adapter.get_connection_info(self.engine)
136
+ logger.info(
137
+ f"Database plugin initialized successfully - "
138
+ f"Driver: {connection_info.get('driver')}, "
139
+ f"Database: {connection_info.get('database')}, "
140
+ f"Version: {connection_info.get('version', 'unknown')}"
141
+ )
142
+
143
+ except Exception as e:
144
+ logger.error(f"Failed to initialize database plugin: {e}", exc_info=True)
145
+ raise RuntimeError(f"Database plugin startup failed: {e}") from e
146
+
147
+ async def shutdown(self, app: "FastAPI") -> None:
148
+ """
149
+ Clean up database resources during application shutdown.
150
+
151
+ Properly disposes of the async engine and cleans up connections.
152
+
153
+ Args:
154
+ app: FastAPI application instance
155
+ """
156
+ logger = get_app_logger()
157
+
158
+ try:
159
+ if self.engine:
160
+ logger.debug("Disposing database engine...")
161
+ await self.engine.dispose()
162
+ logger.info("Database engine disposed successfully")
163
+
164
+ # Clean up app state
165
+ if hasattr(app.state, "db_engine"):
166
+ del app.state.db_engine
167
+ if hasattr(app.state, "db_session"):
168
+ del app.state.db_session
169
+ if hasattr(app.state, "db_adapter"):
170
+ del app.state.db_adapter
171
+
172
+ except Exception as e:
173
+ logger.error(f"Error during database plugin shutdown: {e}", exc_info=True)
174
+
175
+ async def get_health_status(self, app: "FastAPI") -> dict[str, Any]:
176
+ """
177
+ Get database health status for monitoring.
178
+
179
+ Args:
180
+ app: FastAPI application instance
181
+
182
+ Returns:
183
+ Dictionary with database health information
184
+ """
185
+ if not self.engine:
186
+ return {
187
+ "healthy": False,
188
+ "error": "Database engine not initialized",
189
+ "adapter": self.adapter.__class__.__name__,
190
+ }
191
+
192
+ try:
193
+ is_healthy = await self.adapter.health_check(self.engine)
194
+ connection_info = await self.adapter.get_connection_info(self.engine)
195
+
196
+ return {
197
+ "healthy": is_healthy,
198
+ "adapter": self.adapter.__class__.__name__,
199
+ "connection_string": self._mask_connection_string(),
200
+ **connection_info,
201
+ }
202
+ except Exception as e:
203
+ return {
204
+ "healthy": False,
205
+ "error": str(e),
206
+ "adapter": self.adapter.__class__.__name__,
207
+ }
208
+
209
+ def _mask_connection_string(self) -> str:
210
+ """
211
+ Mask sensitive information in connection string for logging.
212
+
213
+ Returns:
214
+ Connection string with password masked
215
+ """
216
+ if "://" not in self.connection_string:
217
+ return self.connection_string
218
+
219
+ parts = self.connection_string.split("://", 1)
220
+ if len(parts) != 2:
221
+ return self.connection_string
222
+
223
+ scheme, rest = parts
224
+ if "@" not in rest:
225
+ return self.connection_string
226
+
227
+ # Mask password in user:pass@host format
228
+ user_part, host_part = rest.split("@", 1)
229
+ if ":" in user_part:
230
+ user, _ = user_part.split(":", 1)
231
+ masked_user_part = f"{user}:***"
232
+ else:
233
+ masked_user_part = user_part
234
+
235
+ return f"{scheme}://{masked_user_part}@{host_part}"