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,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}"
|