redis implemntations and api setup completed

This commit is contained in:
2025-01-25 20:59:47 +03:00
parent 32022ca521
commit 3d5a43220e
138 changed files with 2888 additions and 1117 deletions

View File

@@ -7,15 +7,16 @@ making common utilities and base classes available for all API services.
from .abstract_class import (
MethodToEvent,
PageInfo,
ClusterToMethod,
CategoryCluster,
Event,
)
# from .base_request_model import BaseRequestModel, DictRequestModel
# Re-export commonly used classes
__all__ = [
"MethodToEvent",
"PageInfo",
"ClusterToMethod",
"CategoryCluster",
"Event",
]

View File

@@ -1,153 +1,8 @@
from typing import Any, ClassVar, Dict, List, Optional
from abc import abstractmethod
from typing import Any, Dict, List, Optional, Callable
from uuid import UUID
from .pageinfo import PageInfo
class Event:
KEY_: str # static string uuid.uuid4().__str__()
RESPONSE_VALIDATOR: ClassVar["PydanticModel"]
REQUEST_VALIDATOR: ClassVar["PydanticModel"]
DESCRIPTION: str
EXTRA_OPTIONS: Optional[Dict[str, Any]] = None
def __init__(
self, key: UUID, request_validator: "PydanticModel", response_validator: "PydanticModel",
description: str, extra_options: Optional[Dict[str, Any]] = None
) -> None:
self.KEY_ = key
self.REQUEST_VALIDATOR = request_validator
self.RESPONSE_VALIDATOR = response_validator
self.DESCRIPTION = description
self.EXTRA_OPTIONS = extra_options
@property
def name(self):
return f"This is an event of {self.__class__.__name__}. Description: {self.DESCRIPTION}"
@property
def key(self):
return str(self.KEY_)
@abstractmethod
def endpoint_callable(request: "Request", data: Any):
"""
return cls.retrieve_event(event_function_code).retrieve_callable(token_dict, data)
"""
pass
class MethodToEvent:
"""
for all endpoint callable
def endpoint_callable(request: Request, data: PydanticModel):
return cls.retrieve_event(event_function_code).retrieve_callable(token_dict, data)
"""
EVENTS: list[Event]
HEADER_LANGUAGE_MODELS: list[Dict] # [Table.__language_model__ | Dict[__language_model__]]
ERRORS_LANGUAGE_MODELS: Optional[list[Dict]] # [Dict[ErrorCode][lang]]
URL: str
METHOD: str
SUMMARY: str
DESCRIPTION: str
EXTRA_OPTIONS: Optional[Dict[str, Any]] = None
def __init__(
self,
events: list[Event],
headers: list[Dict],
url: str,
method: str,
summary: str,
description: str,
errors: Optional[list[Dict]] = None,
extra_options: Optional[Dict[str, Any]] = None,
):
self.EVENTS = events
self.URL = url
self.METHOD = method
self.SUMMARY = summary
self.DESCRIPTION = description
self.HEADER_LANGUAGE_MODELS = headers
self.ERRORS_LANGUAGE_MODELS = errors
self.EXTRA_OPTIONS = extra_options
def retrieve_all_event_keys():
"""
self.EVENTS.iter()
[FUNCTION_CODE]
"""
pass
def retrieve_event(event_function_code: str):
if list_found := [event for event in self.EVENTS if str(event.key) == event_function_code]:
return list_found[0]
raise ValueError(f"Event with function code {event_function_code} not found")
def retrieve_redis_value() -> Dict:
"""
Key(f"METHOD_FUNCTION_CODES:{ClusterToMethod}:MethodEvent:Endpoint") : Value([FUNCTION_CODE, ...])
"""
pass
class CategoryCluster:
TAGS: list
PREFIX: str
PAGEINFO: PageInfo
DESCRIPTION: str
ENDPOINTS: list = [MethodToEvent]
SUBCATEGORY: Optional[List["CategoryCluster"]] = []
INCLUDE_IN_SCHEMA: Optional[bool] = True
def __init__(
self,
tags: list,
prefix: str,
description: str,
pageinfo: PageInfo,
endpoints: list,
sub_category: list = [],
include_in_schema: Optional[bool] = True,
):
self.TAGS = tags
self.PREFIX = prefix
self.PAGEINFO = pageinfo
self.DESCRIPTION = description
self.ENDPOINTS = endpoints or []
self.SUBCATEGORY = sub_category or []
self.INCLUDE_IN_SCHEMA = include_in_schema
def retrieve_all_function_codes():
"""
[FUNCTION_CODE, ...]
self.ENDPOINTS -> iter()
"""
pass
def retrieve_page_info():
"""
PAGE_INFO:ClusterToMethod = {
"PageInfo": {...}
"subCategory": PAGE_INFO:ClusterToMethod
}
PAGE_INFO:ClusterToMethod = {
"PageInfo": {...}
"subCategory": PAGE_INFO:ClusterToMethod
}
"""
pass
def retrieve_redis_value() -> Dict:
"""
Key(CLUSTER_FUNCTION_CODES:ClusterToMethod) : Value(PAGE_INFO, [FUNCTION_CODE, ...])
"""
pass
from ApiLayers.AllConfigs.Redis.configs import RedisCategoryKeys
class PageInfo:
@@ -155,7 +10,7 @@ class PageInfo:
NAME: str
BUTTON_NAME: str
PAGE_URL: str
PAGEINFO: "PageInfo"
PAGEINFO: Dict[str, Any]
def __init__(
self,
@@ -173,4 +28,195 @@ class PageInfo:
self.PARENT = parent
class Event:
KEY_: str # static string uuid.uuid4().__str__()
RESPONSE_VALIDATOR: Optional[Any]
REQUEST_VALIDATOR: Optional[Any]
DESCRIPTION: str
EXTRA_OPTIONS: Optional[Dict[str, Any]] = None
endpoint_callable: Any
def __init__(
self,
name: str,
key: str | UUID,
description: str,
request_validator: Optional[Any] = None,
response_validator: Optional[Any] = None,
extra_options: Optional[Dict[str, Any]] = None,
) -> None:
self.NAME = name
self.KEY_ = key
self.REQUEST_VALIDATOR = request_validator
self.RESPONSE_VALIDATOR = response_validator
self.DESCRIPTION = description
self.EXTRA_OPTIONS = extra_options
@property
def description(self):
return f"This is an event of {self.name}. Description: {self.DESCRIPTION}"
@property
def name(self):
return self.NAME
@property
def key(self):
return str(self.KEY_)
@abstractmethod
def endpoint_callable(self, **kwargs) -> Any:
"""
Retrieves the endpoint function based on the event key.
"""
return self.endpoint_callable(**kwargs)
class MethodToEvent:
"""
for all endpoint callable
def endpoint_callable(request: Request, data: PydanticModel):
return cls.retrieve_event(event_function_code).retrieve_callable(token_dict, data)
[Table.__language_model__ | Dict[__language_model__]]
[Dict[ErrorCode][lang]]
"""
EVENTS: dict[str, Event]
HEADER_LANGUAGE_MODELS: list[Dict]
ERRORS_LANGUAGE_MODELS: Optional[list[Dict]]
URL: str
METHOD: str
SUMMARY: str
DESCRIPTION: str
DECORATORS_LIST: Optional[Callable] = []
EXTRA_OPTIONS: Optional[Dict[str, Any]] = None
def __init__(
self,
name: str,
events: dict[str, Event],
headers: list[Dict],
url: str,
method: str,
summary: str,
description: str,
decorators_list: Optional[List[Callable]] = None,
errors: Optional[list[Dict]] = None,
extra_options: Optional[Dict[str, Any]] = None,
):
self.EVENTS = events
self.URL = url
self.METHOD = method
self.SUMMARY = summary
self.NAME = name
self.DESCRIPTION = description
self.DECORATORS_LIST = decorators_list
self.HEADER_LANGUAGE_MODELS = headers
self.ERRORS_LANGUAGE_MODELS = errors
self.EXTRA_OPTIONS = extra_options
@property
def name(self):
return self.NAME
def retrieve_all_event_keys(self):
"""
Retrieves all event keys from the events list.
"""
return [str(event_key) for event_key in self.EVENTS.keys()]
def retrieve_event(self, event_function_code: str) -> Event:
"""
Retrieves the event object from the events list based on the event function code.
"""
if found_event := self.EVENTS.get(event_function_code, None):
return found_event
raise ValueError(f"Event with function code {event_function_code} not found")
def retrieve_redis_value(self, cluster_name: str) -> Dict:
"""
Key("METHOD_FUNCTION_CODES:{ClusterToMethod}:MethodEvent:Endpoint") : Value([FUNCTION_CODE, ...])
"""
redis_key = f"{RedisCategoryKeys.METHOD_FUNCTION_CODES}:{cluster_name}:{self.name}:{self.URL}"
return {redis_key: self.retrieve_all_event_keys()}
@staticmethod
def endpoint_callable(**kwargs):
"""
return cls.retrieve_event(event_function_code).retrieve_callable(token_dict, data)
"""
raise NotImplementedError("Endpoint callable method is not implemented")
class CategoryCluster:
TAGS: list
PREFIX: str
PAGEINFO: PageInfo
DESCRIPTION: str
ENDPOINTS: list[MethodToEvent] # [MethodToEvent, ...]
SUBCATEGORY: Optional[List["CategoryCluster"]] # [CategoryCluster, ...]
INCLUDE_IN_SCHEMA: Optional[bool] = True
def __init__(
self,
name: str,
tags: list,
prefix: str,
description: str,
pageinfo: PageInfo,
endpoints: list[MethodToEvent],
sub_category: list,
include_in_schema: Optional[bool] = True,
):
self.NAME = name
self.TAGS = tags
self.PREFIX = prefix
self.PAGEINFO = pageinfo
self.DESCRIPTION = description
self.ENDPOINTS = endpoints or []
self.SUBCATEGORY = sub_category or []
self.INCLUDE_IN_SCHEMA = include_in_schema
@property
def name(self):
return self.NAME
def get_redis_cluster_index_value(self):
"""
RedisCategoryKeys.CLUSTER_2_METHOD_EVENT
Returns the class name and function codes for the class.
"""
dict_cluster_2_method, list_endpoints = {}, [i.name for i in self.ENDPOINTS]
for endpoint_name in list_endpoints:
dict_cluster_2_method[endpoint_name] = self.name
dict_cluster_2_method[self.name] = list_endpoints
return dict_cluster_2_method
def retrieve_all_function_codes(self):
"""
Retrieves all function codes by iterating over the events list.
"""
all_function_codes = []
for event_method in self.ENDPOINTS:
all_function_codes.extend([str(event_key) for event_key in event_method.EVENTS.keys()])
return all_function_codes
def retrieve_redis_value(self) -> Dict:
"""
Create Redis Key and Value from function codes
Key(CLUSTER_FUNCTION_CODES:ClusterToMethod) : Value(PAGE_INFO, [FUNCTION_CODE, ...])
"""
return {
f"{RedisCategoryKeys.CLUSTER_FUNCTION_CODES}:{self.name}": self.retrieve_all_function_codes()
}
def retrieve_page_info(self):
"""
PAGE_INFO:ClusterToMethod = {"PageInfo": {...}, "subCategory": PAGE_INFO:ClusterToMethod}
"""
raise NotImplementedError(
"CategoryCluster retrieve_page_info() method is not implemented"
)

View File

@@ -0,0 +1,42 @@
from Events.Engine import CategoryCluster
class CategoryBulk:
def __init__(self, category_cluster: CategoryCluster = None, name: str = ""):
self.category_cluster = category_cluster
self.name = name
class CategoryClusterController:
imports_dict: list[CategoryBulk] = []
@property
def imports(self):
return self.imports_dict
@classmethod
def import_all_category_clusters(cls, category_clusters):
"""
Imports all category clusters from the given list
{ "category_cluster_name": "category_cluster_module" }
"""
if not hasattr(category_clusters, "__all__"):
raise ValueError(f"Given module {str(category_clusters)} does not have __all__ attribute")
for iter_module in [str(item) for item in category_clusters.__all__]:
# CategoryCluster which represent api routers for each category
cls.imports_dict.append(
CategoryBulk(
category_cluster=getattr(category_clusters, iter_module, None),
name=iter_module
))
@classmethod
def as_dict(cls):
to_dict = {}
for cluster in cls.imports_dict:
to_dict[cluster.name] = cluster.category_cluster
return to_dict
cluster_controller = CategoryClusterController()

View File

@@ -0,0 +1,84 @@
from ApiLayers.AllConfigs.Redis.configs import RedisCategoryKeys
class PrepareRedisItems:
MENU_FIRST_LAYER_KEY: str = RedisCategoryKeys.MENU_FIRST_LAYER
MENU_FIRST_LAYER_VALUE: set[str] = set()
CLUSTER_INDEX_KEY: str = RedisCategoryKeys.CLUSTER_INDEX
CLUSTER_INDEX_VALUE: dict = {}
CLUSTER_FUNCTION_CODES_KEY: str = RedisCategoryKeys.CLUSTER_FUNCTION_CODES
CLUSTER_FUNCTION_CODES_VALUE: dict = {}
METHOD_FUNCTION_CODES_KEY: str = RedisCategoryKeys.METHOD_FUNCTION_CODES
METHOD_FUNCTION_CODES_VALUE: dict = {}
ENDPOINT2CLASS_KEY: str = RedisCategoryKeys.ENDPOINT2CLASS
ENDPOINT2CLASS_VALUE: dict = {}
@property
def as_dict(self):
return {
self.MENU_FIRST_LAYER_KEY: list(self.MENU_FIRST_LAYER_VALUE),
self.CLUSTER_INDEX_KEY: self.CLUSTER_INDEX_VALUE,
self.CLUSTER_FUNCTION_CODES_KEY: self.CLUSTER_FUNCTION_CODES_VALUE,
self.METHOD_FUNCTION_CODES_KEY: self.METHOD_FUNCTION_CODES_VALUE,
self.ENDPOINT2CLASS_KEY: self.ENDPOINT2CLASS_VALUE,
}
class DecoratorModule:
@staticmethod
def get_all_decorators(func):
"""
Get all decorators of a function, excluding the original function itself.
Returns a list of decorator functions in the order they were applied.
"""
decorators = []
current_func = func
original_qualname = getattr(func, '__qualname__', '')
while hasattr(current_func, '__wrapped__'):
if hasattr(current_func, '__closure__') and current_func.__closure__:
for cell in current_func.__closure__:
decorator = cell.cell_contents
# Only add if it's a callable and not the original function
if callable(decorator) and getattr(decorator, '__qualname__', '') != original_qualname:
decorators.append(decorator)
current_func = current_func.__wrapped__
return list(dict.fromkeys(decorators)) # Remove duplicates while preserving order
@staticmethod
def get_actual_decorators(method_endpoint):
original_qualname = getattr(method_endpoint.endpoint_callable, '__qualname__', '')
actual_decorators = [
d for d in method_endpoint.DECORATORS_LIST or []
if callable(d) and getattr(d, '__qualname__', '') != original_qualname
]
return actual_decorators
@classmethod
def apply_decorators(cls, method_endpoint):
# Get the original function and its qualname
function_callable = method_endpoint.endpoint_callable
# Filter out the original function and apply decorators
actual_decorators = cls.get_actual_decorators(method_endpoint)
# Apply decorators in reverse order (to match @ syntax behavior)
for decorator in reversed(actual_decorators):
try:
function_callable = decorator(function_callable)
except Exception as e:
print(f"Warning: Failed to apply decorator {decorator.__qualname__}: {str(e)}")
method_endpoint.endpoint_callable = function_callable
# Get the final list of applied decorators (for debugging)
applied_decorators = cls.get_all_decorators(method_endpoint.endpoint_callable)
applied_decorators_qualname = [getattr(d, '__qualname__', str(d)) for d in applied_decorators]
if applied_decorators:
print(f"Applied decorators for {method_endpoint.name}:", applied_decorators_qualname)
return applied_decorators_qualname
@classmethod
def list_qualname(cls, method_endpoint_list):
return [getattr(method_endpoint, '__qualname__', '') for method_endpoint in method_endpoint_list]

View File

@@ -1,2 +1,16 @@
from Events.AllEvents.events_file import events_list
from .category_cluster_models import cluster_controller
def get_cluster_controller_group():
for cluster in events_list:
cluster_controller.import_all_category_clusters(cluster)
return cluster_controller
"""
prepare_routing = PrepareRouting(cluster_controller_group=cluster_controller)
prepare_events = PrepareEvents(cluster_controller_group=cluster_controller)
set_items_2_redis = SetItems2Redis(prepare_events=prepare_events)
print(set_items_2_redis)
print(prepare_routing)
"""

View File

@@ -1,7 +1,171 @@
import AllEvents.auth as auths_events
import AllEvents.events as events_events
import AllEvents.validations as validations_events
from typing import Any
from ApiLayers.ApiServices.Cluster.create_router import (
CreateRouterFromCluster,
CreateEndpointFromCluster
)
from Events.Engine.abstract_class import CategoryCluster
from Services.Redis.Actions.actions import RedisActions
from Services.Redis.Models.cluster import RedisList
from .prepare_redis_items import DecoratorModule, PrepareRedisItems
from .category_cluster_models import CategoryClusterController
for event in [*auths_events.__all__, *events_events.__all__, *validations_events.__all__]:
print(event)
class PrepareRouting(DecoratorModule):
__routers_list: list[Any] = list()
__endpoints_list: list[Any] = list()
__safe_endpoint_list: list[Any] = list()
def __init__(self, cluster_controller_group: CategoryClusterController):
self.cluster_controller_group = cluster_controller_group
self.prepare_needs()
def __str__(self):
return f"\nPrepared Routing:\n\n{self.routers}\n\n{self.endpoints}\n\n{self.safe_endpoints}\n"
@property
def routers(self):
return self.__routers_list
@property
def endpoints(self):
return self.__endpoints_list
@property
def safe_endpoints(self):
return self.__safe_endpoint_list
def create_endpoints(self, cluster: CategoryCluster, created_router):
for method_endpoint in list(cluster.ENDPOINTS):
# Filter out the original function and apply decorators
applied_decorators_qualname = self.apply_decorators(method_endpoint)
# Register the endpoint with FastAPI router
create_endpoint = CreateEndpointFromCluster(
router=created_router, method_endpoint=method_endpoint
)
created_router = create_endpoint.router
if "MiddlewareModule" in applied_decorators_qualname:
self.__safe_endpoint_list.append(method_endpoint)
self.__endpoints_list.append(method_endpoint)
def create_router(self, cluster: CategoryCluster):
### Create Router Parameters create router for each cluster
created_router = CreateRouterFromCluster(
prefix=cluster.PREFIX,
tags=cluster.TAGS,
include_in_schema=cluster.INCLUDE_IN_SCHEMA,
)
self.__routers_list.append(created_router.router)
return created_router.router
def prepare_needs(self):
# @Pages iterate(ClusterToMethod)
for cluster_control in self.cluster_controller_group.imports:
cluster = cluster_control.category_cluster
created_router = self.create_router(cluster)
self.create_endpoints(cluster, created_router)
class PrepareEvents(DecoratorModule):
def __init__(self, cluster_controller_group: CategoryClusterController):
self.cluster_controller_group = cluster_controller_group
self.valid_redis_items: PrepareRedisItems = PrepareRedisItems()
self.prepare_needs()
def prepare_needs(self):
# @Pages iterate(ClusterToMethod)
for cluster_control in self.cluster_controller_group.imports:
cluster = cluster_control.category_cluster
### Create Redis Parameters
# [SAVE]REDIS => MENU_FIRST_LAYER = [ClusterToMethod, ...]
self.valid_redis_items.MENU_FIRST_LAYER_VALUE.add(cluster.name)
# [SAVE]REDIS => CLUSTER_INDEX = {ClusterToMethod: [MethodEvent, ...], "MethodEvent": "ClusterToMethod"}
self.valid_redis_items.CLUSTER_INDEX_VALUE.update(cluster.get_redis_cluster_index_value())
# [SAVE]REDIS => CLUSTER_FUNCTION_CODES = {"ClusterToMethod"} : [FUNCTION_CODE, ...]}
self.valid_redis_items.CLUSTER_FUNCTION_CODES_VALUE = {
f"{self.valid_redis_items.CLUSTER_FUNCTION_CODES_KEY}:{cluster.name}" : tuple(cluster.retrieve_all_function_codes())
}
for method_endpoint in list(cluster.ENDPOINTS):
# [SAVE]REDIS => ENDPOINT2CLASS = {MethodEvent: Endpoint("/.../.../..."), ...}
self.valid_redis_items.ENDPOINT2CLASS_VALUE.update(
{f"{cluster.name}:{method_endpoint.name}": method_endpoint.URL}
)
self.valid_redis_items.ENDPOINT2CLASS_VALUE.update(
{method_endpoint.URL :f"{cluster.name}:{method_endpoint.name}"}
)
# [SAVE]REDIS => METHOD_FUNCTION_CODES:MethodEvent:Endpoint = [FUNCTION_CODE, ...]
self.valid_redis_items.METHOD_FUNCTION_CODES_VALUE.update(
method_endpoint.retrieve_redis_value(cluster_name=cluster.name)
)
class SetItems2Redis:
std_out: str = ""
def __init__(self, prepare_events: PrepareEvents):
self.prepare_events = prepare_events
self.set_items()
def __str__(self):
self.std_out = f"\nSetItems2Redis:\n\n{self.std_out}"
return self.std_out
def set_items(self):
from ApiLayers.AllConfigs.Redis.configs import RedisCategoryKeys
dict_prep = self.prepare_events.valid_redis_items.as_dict
RedisActions.delete(
list_keys=[
f"{RedisCategoryKeys.MENU_FIRST_LAYER}:*",
f"{RedisCategoryKeys.CLUSTER_INDEX}:*",
f"{RedisCategoryKeys.CLUSTER_FUNCTION_CODES}:*",
f"{RedisCategoryKeys.METHOD_FUNCTION_CODES}:*"
f"{RedisCategoryKeys.ENDPOINT2CLASS}:*",
]
)
# Save MENU_FIRST_LAYER to Redis
redis_list = RedisList(redis_key=RedisCategoryKeys.MENU_FIRST_LAYER)
RedisActions.set_json(
list_keys=redis_list.to_list(), value=dict_prep.get(RedisCategoryKeys.MENU_FIRST_LAYER)
)
self.std_out = f"{RedisCategoryKeys.MENU_FIRST_LAYER}: {dict_prep.get(RedisCategoryKeys.MENU_FIRST_LAYER)}\n"
# Save CLUSTER_INDEX to Redis
redis_list = RedisList(redis_key=RedisCategoryKeys.CLUSTER_INDEX)
RedisActions.set_json(
list_keys=redis_list.to_list(), value=dict_prep.get(RedisCategoryKeys.CLUSTER_INDEX)
)
self.std_out += f"\n{RedisCategoryKeys.CLUSTER_INDEX}: {dict_prep.get(RedisCategoryKeys.CLUSTER_INDEX)}\n"
# Save CLUSTER_FUNCTION_CODES to Redis by iterating over the dict
for redis_key, redis_value in dict_prep.get(RedisCategoryKeys.CLUSTER_FUNCTION_CODES).items():
redis_list = RedisList(redis_key=redis_key)
RedisActions.set_json(
list_keys=redis_list.to_list(), value=list(redis_value)
)
self.std_out += f"\n{RedisCategoryKeys.CLUSTER_FUNCTION_CODES}: {dict_prep.get(RedisCategoryKeys.CLUSTER_FUNCTION_CODES)}\n"
# Save METHOD_FUNCTION_CODES to Redis by iterating over the dict
for redis_key, redis_value in dict_prep.get(RedisCategoryKeys.METHOD_FUNCTION_CODES).items():
redis_list = RedisList(redis_key=redis_key)
RedisActions.set_json(
list_keys=redis_list.to_list(), value=list(redis_value)
)
self.std_out += f"\n{RedisCategoryKeys.METHOD_FUNCTION_CODES}: {dict_prep.get(RedisCategoryKeys.METHOD_FUNCTION_CODES)}\n"
# Save ENDPOINT2CLASS to Redis by iterating over the dict
for redis_key, redis_value in dict_prep.get(RedisCategoryKeys.ENDPOINT2CLASS).items():
redis_list = RedisList(redis_key=f"{RedisCategoryKeys.ENDPOINT2CLASS}:{redis_key}\n")
RedisActions.set_json(
list_keys=redis_list.to_list(), value=redis_value
)
self.std_out += f"\n{RedisCategoryKeys.ENDPOINT2CLASS}: {dict_prep.get(RedisCategoryKeys.ENDPOINT2CLASS)}\n"
RedisActions.set_json(
list_keys=[f"{RedisCategoryKeys.REBUILD}:*"], value=False
)