event decarotor checked & event 2 endpoint dynmc create is tested

This commit is contained in:
2025-01-15 23:40:55 +03:00
parent 76d286b519
commit 049a7c1e11
12 changed files with 369 additions and 173 deletions

View File

@@ -1,4 +1,9 @@
import typing
from collections.abc import Callable
from fastapi import Request
from typing import Dict, Any
from ApiValidations.Custom.token_objects import (
OccupantTokenObject,
EmployeeTokenObject,
@@ -17,12 +22,14 @@ from ApiValidations.Request import (
ListOptions,
)
from Services.PostgresDb.Models.alchemy_response import AlchemyJsonResponse
from ApiEvents.abstract_class import (
from ApiValidations.Response import AccountRecordResponse
from events.abstract_class import (
MethodToEvent,
RouteFactoryConfig,
EndpointFactoryConfig,
)
from ApiValidations.Response import AccountRecordResponse
# from events.utils import with_token_event
class AccountRecordsListEventMethods(MethodToEvent):
@@ -355,3 +362,108 @@ class AccountRecordsPatchEventMethods(MethodToEvent):
message="Account record patched successfully",
result=account_record,
)
def address_list(request: Request, data: dict) -> Dict[str, Any]:
"""Handle address list endpoint."""
# Access context through the handler
handler = address_list.handler
handler_context = address_list.handler.context
function_name = AccountRecordsListEventMethods.__event_keys__.get(handler.function_code)
original_function = getattr(AccountRecordsListEventMethods, function_name)
# original_function(data, request)
return {
"data": data,
"function_code": handler.function_code, # This will be the URL
"token_dict": handler_context.get('token_dict'),
"url_of_endpoint": handler_context.get('url_of_endpoint'),
"request": str(request.headers),
}
def address_create(request: Request, data: dict):
"""Handle address creation endpoint."""
return {
"data": data,
"request": str(request.headers),
"request_url": str(request.url),
"request_base_url": str(request.base_url),
}
def address_search(request: Request, data: dict):
"""Handle address search endpoint."""
# Get function_code from the wrapper's closure
function_code = address_search.function_code
return {
"data": data,
"function_code": function_code
}
def address_update(request: Request, address_uu_id: str, data: dict):
"""Handle address update endpoint."""
# Get function_code from the wrapper's closure
function_code = address_update.function_code
return {
"address_uu_id": address_uu_id,
"data": data,
"function_code": function_code
}
# Account Records Router Configuration
ACCOUNT_RECORDS_CONFIG = RouteFactoryConfig(
name='account_records',
prefix='/account/records',
tags=['Account Records'],
include_in_schema=True,
endpoints=[
EndpointFactoryConfig(
url_prefix = "/account/records",
url_endpoint="/address/list",
url_of_endpoint = "/account/records/address/list",
endpoint="/address/list",
method="POST",
summary="List Active/Delete/Confirm Address",
description="List Active/Delete/Confirm Address",
is_auth_required=True,
is_event_required=True,
endpoint_function=address_list
),
EndpointFactoryConfig(
url_prefix = "/account/records",
url_endpoint="/address/create",
url_of_endpoint = "/account/records/address/create",
endpoint="/address/create",
method="POST",
summary="Create Address with given auth levels",
description="Create Address with given auth levels",
is_auth_required=False,
is_event_required=False,
endpoint_function=address_create
),
EndpointFactoryConfig(
url_prefix = "/account/records",
url_endpoint="/address/search",
url_of_endpoint = "/account/records/address/search",
endpoint="/address/search",
method="POST",
summary="Search Address with given auth levels",
description="Search Address with given auth levels",
is_auth_required=True,
is_event_required=True,
endpoint_function=address_search
),
EndpointFactoryConfig(
url_prefix = "/account/records",
url_endpoint="/address/update/{address_uu_id}",
url_of_endpoint="/account/records/address/update/{address_uu_id}",
endpoint="/address/update/{address_uu_id}",
method="PUT",
summary="Update Address with given auth levels",
description="Update Address with given auth levels",
is_auth_required=True,
is_event_required=True,
endpoint_function=address_update
)
]
).as_dict()

View File

@@ -5,93 +5,13 @@ This module collects and registers all route configurations from different modul
to be used by the dynamic route creation system.
"""
from typing import Dict, List, Any, Callable
from fastapi import Request
from typing import Dict, List, Any
from events.account.account_records import ACCOUNT_RECORDS_CONFIG
from ApiEvents.abstract_class import RouteFactoryConfig, EndpointFactoryConfig
from ApiEvents.EventServiceApi.utils import with_token_event
from ApiEvents.EventServiceApi.account.account_records import (
AccountRecordsListEventMethods,
ListOptions,
InsertAccountRecord,
SearchAddress,
UpdateAccountRecord,
)
@with_token_event
def address_list(request: Request, list_options: ListOptions):
"""Handle address list endpoint."""
pass
@with_token_event
def address_create(request: Request, data: InsertAccountRecord):
"""Handle address creation endpoint."""
pass
@with_token_event
def address_search(request: Request, data: SearchAddress):
"""Handle address search endpoint."""
pass
@with_token_event
def address_update(request: Request, address_uu_id: str, data: UpdateAccountRecord):
"""Handle address update endpoint."""
pass
# Account Records Router Configuration
ACCOUNT_RECORDS_CONFIG = {
'name': 'account_records',
'prefix': '/account/records',
'tags': ['Account Records'],
'include_in_schema': True,
'endpoints': [
EndpointFactoryConfig(
endpoint="/list",
method="POST",
summary="List Active/Delete/Confirm Address",
description="List Active/Delete/Confirm Address",
is_auth_required=True,
is_event_required=True,
request_model=ListOptions,
endpoint_function=address_list
),
EndpointFactoryConfig(
endpoint="/create",
method="POST",
summary="Create Address with given auth levels",
description="Create Address with given auth levels",
is_auth_required=True,
is_event_required=True,
request_model=InsertAccountRecord,
endpoint_function=address_create
),
EndpointFactoryConfig(
endpoint="/search",
method="POST",
summary="Search Address with given auth levels",
description="Search Address with given auth levels",
is_auth_required=True,
is_event_required=True,
request_model=SearchAddress,
endpoint_function=address_search
),
EndpointFactoryConfig(
endpoint="/update/{address_uu_id}",
method="POST",
summary="Update Address with given auth levels",
description="Update Address with given auth levels",
is_auth_required=True,
is_event_required=True,
request_model=UpdateAccountRecord,
endpoint_function=address_update
)
]
}
# Registry of all route configurations
ROUTE_CONFIGS = [
ACCOUNT_RECORDS_CONFIG,
# Add other route configurations here
]
def get_route_configs() -> List[Dict[str, Any]]:

View File

@@ -1,51 +0,0 @@
"""
Utility functions for API event handling.
"""
from functools import wraps
from inspect import signature
from typing import Callable, TypeVar, ParamSpec, Any
from fastapi import Request
from Services.PostgresDb.Models.token_models import parse_token_object_to_dict
P = ParamSpec('P')
R = TypeVar('R')
def with_token_event(func: Callable[P, R]) -> Callable[P, R]:
"""
Decorator that handles token parsing and event execution.
This decorator:
1. Parses the token from the request
2. Calls the appropriate event with the token and other arguments
Args:
func: The endpoint function to wrap
Returns:
Wrapped function that handles token parsing and event execution
"""
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
# Extract request from args or kwargs
request = next(
(arg for arg in args if isinstance(arg, Request)),
kwargs.get('request')
)
if not request:
raise ValueError("Request object not found in arguments")
# Parse token
token_dict = parse_token_object_to_dict(request=request)
# Add token_dict to kwargs
kwargs['token_dict'] = token_dict
# Call the original function
return token_dict.available_event(**{
k: v for k, v in kwargs.items()
if k in signature(token_dict.available_event).parameters
})
return wrapper

View File

@@ -5,10 +5,11 @@ This module provides core abstractions for route configuration and factory,
with support for authentication and event handling.
"""
from typing import Optional, Dict, Any, List, Type, Union, ClassVar, Tuple, TypeVar
from typing import Optional, Dict, Any, List, Type, Union, ClassVar, Tuple, TypeVar, Callable
from dataclasses import dataclass, field
from pydantic import BaseModel
ResponseModel = TypeVar('ResponseModel', bound=BaseModel)
@@ -17,25 +18,44 @@ class EndpointFactoryConfig:
"""Configuration class for API endpoints.
Attributes:
url_of_endpoint: Full URL path for this endpoint
endpoint: URL path for this endpoint
method: HTTP method (GET, POST, etc.)
summary: Short description for API documentation
description: Detailed description for API documentation
endpoint_function: Function to handle the endpoint
is_auth_required: Whether authentication is required
is_event_required: Whether event handling is required
request_model: Expected request model type
extra_options: Additional endpoint options
response_model: Expected response model type
"""
url_prefix :str
url_endpoint: str
url_of_endpoint: str
endpoint: str
method: str
summary: str
description: str
is_auth_required: bool
is_event_required: bool
request_model: Type[BaseModel]
endpoint_function: Callable
is_auth_required: bool = True
is_event_required: bool = False
extra_options: Dict[str, Any] = field(default_factory=dict)
response_model: Optional[Type[BaseModel]] = None
def __post_init__(self):
"""Post-initialization processing.
Apply appropriate wrappers based on auth and event requirements:
- If both auth and event required -> wrap with with_token_event
- If only auth required -> wrap with MiddlewareModule.auth_required
"""
# Store url_of_endpoint for the handler
self.endpoint_function.url_of_endpoint = self.url_of_endpoint
if self.is_auth_required and self.is_event_required:
from events.utils import with_token_event
self.endpoint_function = with_token_event(self.endpoint_function)
elif self.is_auth_required:
from DockerApiServices.AllApiNeeds.middleware.auth_middleware import MiddlewareModule
self.endpoint_function = MiddlewareModule.auth_required(self.endpoint_function)
@dataclass
@@ -75,10 +95,9 @@ class RouteFactoryConfig:
"method": ep.method,
"summary": ep.summary,
"description": ep.description,
"endpoint_function": ep.endpoint_function,
"is_auth_required": ep.is_auth_required,
"is_event_required": ep.is_event_required,
"response_model": ep.response_model.__name__ if ep.response_model else None,
"request_model": ep.request_model.__name__,
"extra_options": ep.extra_options
}
for ep in self.endpoints

104
ApiEvents/utils.py Normal file
View File

@@ -0,0 +1,104 @@
"""
Utility functions for API event handling.
"""
from typing import TypeVar, Callable, Dict, Any
from functools import wraps
from fastapi import Request
R = TypeVar('R')
class BaseEndpointHandler:
"""Base class for handling endpoint execution with context."""
def __init__(self, func: Callable, url_of_endpoint: str):
self.func = func
self.url_of_endpoint = url_of_endpoint
self.function_code = url_of_endpoint # Set initial function_code
self._context = {
'url_of_endpoint': url_of_endpoint,
'function_code': url_of_endpoint, # Initialize with URL
}
@property
def context(self) -> dict:
"""Get the endpoint context."""
return self._context
def update_context(self, **kwargs):
"""Update the endpoint context with new values."""
self._context.update(kwargs)
# Update function_code property if it's in the context
if 'function_code' in kwargs:
self.function_code = kwargs['function_code']
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
class TokenEventHandler(BaseEndpointHandler):
"""Handler for endpoints that require token and event tracking."""
def __init__(self, func: Callable, url_of_endpoint: str):
super().__init__(func, url_of_endpoint)
self.update_context(
token_dict={
'user_id': '1234567890',
'username': 'test_user',
'email': 'asda@email.com',
}
)
class AuthHandler(BaseEndpointHandler):
"""Handler for endpoints that require only authentication."""
def __init__(self, func: Callable, url_of_endpoint: str):
super().__init__(func, url_of_endpoint)
self.update_context(
auth_level="user",
permissions=["read", "write"]
)
def with_token_event(func: Callable[..., Dict[str, Any]]) -> Callable[..., Dict[str, Any]]:
"""Decorator for endpoints with token and event requirements."""
@wraps(func)
def wrapper(*args, **kwargs) -> Dict[str, Any]:
# Create handler with context
handler = TokenEventHandler(
func=func,
url_of_endpoint=func.url_of_endpoint
)
# Update event-specific context
handler.update_context(
function_code=f"7192c2aa-5352-4e36-98b3-dafb7d036a3d" # Keep function_code as URL
)
# Make context available to the function
func.handler = handler
# Call the original function
return func(*args, **kwargs)
return wrapper
def auth_required(func: Callable[..., Dict[str, Any]]) -> Callable[..., Dict[str, Any]]:
"""Decorator for endpoints with only auth requirements."""
@wraps(func)
def wrapper(*args, **kwargs) -> Dict[str, Any]:
# Create handler with context
handler = AuthHandler(
func=func,
url_of_endpoint=func.url_of_endpoint
)
# Make context available to the function
func.handler = handler
# Call the original function
return func(*args, **kwargs)
return wrapper