base context for wrappers updated

This commit is contained in:
berkay 2025-01-17 20:00:53 +03:00
parent 61229cb761
commit 628f6bd483
21 changed files with 404 additions and 300 deletions

View File

@ -19,7 +19,7 @@ class ApiStatic:
class Auth: class Auth:
ACCESS_EMAIL_EXT = "evyos.com.tr" ACCESS_EMAIL_EXT = "evyos.com.tr"
ACCESS_TOKEN_TAG = "evyos-session-key" ACCESS_TOKEN_TAG = "evyos-session-key"
REFRESHER_TOKEN_TAG = "eys_token_refresher" REFRESHER_TOKEN_TAG = "eys-session-refresher"
SECRET_KEY_72 = ( SECRET_KEY_72 = (
"t3sUAmjTGeTgDc6dAUrB41u2SNg0ZHzj4HTjem95y3fRH1nZXOHIBj163kib6iLybT0gLaxq" "t3sUAmjTGeTgDc6dAUrB41u2SNg0ZHzj4HTjem95y3fRH1nZXOHIBj163kib6iLybT0gLaxq"
) )

View File

@ -8,10 +8,22 @@ from typing import TYPE_CHECKING, Union, Dict, Any
from ApiEvents.abstract_class import MethodToEvent from ApiEvents.abstract_class import MethodToEvent
from ApiEvents.base_request_model import DictRequestModel, SuccessResponse from ApiEvents.base_request_model import DictRequestModel, SuccessResponse
from ApiLibrary.common.line_number import get_line_number_for_error from ApiLibrary.common.line_number import get_line_number_for_error
from ApiLibrary.date_time_actions.date_functions import DateTimeLocal
from ApiServices.Login.user_login_handler import UserLoginModule from ApiServices.Login.user_login_handler import UserLoginModule
from ApiServices.Token.token_handler import AccessToken, TokenService from ApiServices.Token.token_handler import TokenService
from ApiValidations.Request.authentication import EmployeeSelectionValidation, Login, OccupantSelectionValidation from ApiValidations.Custom.token_objects import CompanyToken
from ApiValidations.Request.authentication import (
Login,
EmployeeSelectionValidation,
OccupantSelectionValidation, OccupantSelection, EmployeeSelection,
)
from ErrorHandlers import HTTPExceptionApi from ErrorHandlers import HTTPExceptionApi
from Schemas.company.company import Companies
from Schemas.company.department import Departments, Duties, Duty
from Schemas.company.employee import Staff, Employees
from Schemas.event.event import Event2Employee
from Schemas.identity.identity import Users
from Services.Redis.Actions.actions import RedisActions
from .models import ( from .models import (
LoginData, LoginData,
LoginRequestModel, LoginRequestModel,
@ -27,7 +39,10 @@ from .models import (
if TYPE_CHECKING: if TYPE_CHECKING:
from fastapi import Request from fastapi import Request
from ApiServices.Token.token_handler import OccupantTokenObject, EmployeeTokenObject from ApiServices.Token.token_handler import (
OccupantTokenObject,
EmployeeTokenObject
)
# Type aliases for common types # Type aliases for common types
TokenDictType = Union["EmployeeTokenObject", "OccupantTokenObject"] TokenDictType = Union["EmployeeTokenObject", "OccupantTokenObject"]
@ -69,7 +84,7 @@ class AuthenticationLoginEventMethods(MethodToEvent):
# Return response with token and headers # Return response with token and headers
return { return {
"token": token, **token,
"headers": dict(request.headers), "headers": dict(request.headers),
} }
@ -89,18 +104,113 @@ class AuthenticationSelectEventMethods(MethodToEvent):
@classmethod @classmethod
def _handle_employee_selection( def _handle_employee_selection(
cls, cls,
data: SelectionDataEmployee, data: EmployeeSelection,
token_dict: TokenDictType, token_dict: TokenDictType,
request: "Request", request: "Request",
): ):
raise HTTPExceptionApi( Users.set_user_define_properties(token=token_dict)
error_code="", lang="en", loc=get_line_number_for_error() db_session = Users.new_session()
if data.company_uu_id not in token_dict.companies_uu_id_list:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Company not found in token"
)
selected_company = Companies.filter_one(
Companies.uu_id == data.company_uu_id,
db=db_session,
).first
if not selected_company:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Company not found in token"
)
# Get department IDs for the company
department_ids = [
dept.id
for dept in Departments.filter_all(
Departments.company_id == selected_company.id,
db=db_session,
).data
]
# Get duties IDs for the company
duties_ids = [
duty.id
for duty in Duties.filter_all(Duties.company_id == selected_company.id, db=db_session).data
]
# Get staff IDs
staff_ids = [
staff.id for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids), db=db_session).data
]
# Get employee
employee = Employees.filter_one(
Employees.people_id == token_dict.person_id,
Employees.staff_id.in_(staff_ids),
db=db_session,
).first
if not employee:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Employee not found in token"
)
# Get reachable events
reachable_event_list_id = Event2Employee.get_event_id_by_employee_id(
employee_id=employee.id
) )
# Get staff and duties
staff = Staff.filter_one(Staff.id == employee.staff_id, db=db_session).data
duties = Duties.filter_one(Duties.id == staff.duties_id, db=db_session).data
department = Departments.filter_one(Departments.id == duties.department_id, db=db_session).data
# Get bulk duty
bulk_id = Duty.filter_by_one(system=True, duty_code="BULK", db=db_session).data
bulk_duty_id = Duties.filter_by_one(
company_id=selected_company.id,
duties_id=bulk_id.id,
**Duties.valid_record_dict,
db=db_session,
).data
# Create company token
company_token = CompanyToken(
company_uu_id=selected_company.uu_id.__str__(),
company_id=selected_company.id,
department_id=department.id,
department_uu_id=department.uu_id.__str__(),
duty_id=duties.id,
duty_uu_id=duties.uu_id.__str__(),
bulk_duties_id=bulk_duty_id.id,
staff_id=staff.id,
staff_uu_id=staff.uu_id.__str__(),
employee_id=employee.id,
employee_uu_id=employee.uu_id.__str__(),
reachable_event_list_id=reachable_event_list_id,
)
try: # Update Redis
update_token = TokenService.update_token_at_redis(request=request, add_payload=company_token)
return update_token
except Exception as e:
raise HTTPExceptionApi(
error_code="", lang="en", loc=get_line_number_for_error(), sys_msg=f"{e}"
)
@classmethod @classmethod
def _handle_occupant_selection( def _handle_occupant_selection(
cls, cls,
data: SelectionDataOccupant, data: OccupantSelection,
token_dict: TokenDictType, token_dict: TokenDictType,
request: "Request", request: "Request",
): ):
@ -116,25 +226,22 @@ class AuthenticationSelectEventMethods(MethodToEvent):
async def authentication_select_company_or_occupant_type( async def authentication_select_company_or_occupant_type(
cls, cls,
request: "Request", request: "Request",
data: Union[EmployeeSelectionValidation, OccupantSelectionValidation], data: Union[EmployeeSelection, OccupantSelection],
token_dict: TokenDictType, token_dict: TokenDictType,
): ):
"""Handle selection of company or occupant type""" """Handle selection of company or occupant type"""
try: if token_dict.is_employee:
print( return cls._handle_employee_selection(data, token_dict, request)
dict( elif token_dict.is_occupant:
data=data, return cls._handle_occupant_selection(data, token_dict, request)
token_dict=token_dict.model_dump(),
request=dict(request.headers) # except Exception as e:
) # raise HTTPExceptionApi(
) # error_code="HTTP_500_INTERNAL_SERVER_ERROR",
except Exception as e: # lang="en",
raise HTTPExceptionApi( # loc=get_line_number_for_error(),
error_code="HTTP_500_INTERNAL_SERVER_ERROR", # sys_msg=str(e),
lang="en", # )
loc=get_line_number_for_error(),
sys_msg=str(e),
)
class AuthenticationCheckTokenEventMethods(MethodToEvent): class AuthenticationCheckTokenEventMethods(MethodToEvent):

View File

@ -5,7 +5,16 @@ Authentication endpoint configurations.
from typing import TYPE_CHECKING, Dict, Any, Union, Annotated from typing import TYPE_CHECKING, Dict, Any, Union, Annotated
from fastapi import HTTPException, status, Body from fastapi import HTTPException, status, Body
from ApiValidations.Request.authentication import Login from ApiValidations.Request import (
Logout,
Login,
Remember,
Forgot,
CreatePassword,
ChangePassword,
OccupantSelection,
EmployeeSelection,
)
from .auth import ( from .auth import (
AuthenticationChangePasswordEventMethods, AuthenticationChangePasswordEventMethods,
@ -32,7 +41,7 @@ from .models import (
SelectionDataOccupant, SelectionDataOccupant,
RememberRequestModel, RememberRequestModel,
) )
from ApiEvents.base_request_model import DictRequestModel from ApiEvents.base_request_model import DictRequestModel, EndpointBaseRequestModel
from ApiEvents.abstract_class import RouteFactoryConfig, EndpointFactoryConfig, endpoint_wrapper from ApiEvents.abstract_class import RouteFactoryConfig, EndpointFactoryConfig, endpoint_wrapper
if TYPE_CHECKING: if TYPE_CHECKING:
@ -41,30 +50,27 @@ from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTok
# Type aliases for common types # Type aliases for common types
TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject]
@endpoint_wrapper("/authentication/select") @endpoint_wrapper("/authentication/select")
async def authentication_select_company_or_occupant_type( async def authentication_select_company_or_occupant_type(
request: "Request", request: "Request",
data: Union[SelectionDataEmployee, SelectionDataOccupant], data: EndpointBaseRequestModel,
token_dict: TokenDictType = None
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Handle selection of company or occupant type. Select company or occupant type.
Args:
request: The FastAPI request object
data: Selection request data
Returns:
Dict containing the response data
""" """
return { auth_dict = authentication_select_company_or_occupant_type.auth
"headers": dict(request.headers), if data.data.get("company_uu_id"):
"data": data, data = EmployeeSelection(**data.data)
"token": token_dict elif data.data.get("build_living_space_uu_id"):
} data = OccupantSelection(**data.data)
if r := await AuthenticationSelectEventMethods.authentication_select_company_or_occupant_type(
request=request, data=data, token_dict=auth_dict
):
if isinstance(data, EmployeeSelection):
return {"selected_company": data.company_uu_id}
elif isinstance(data, OccupantSelection):
return {"selected_occupant": data.build_living_space_uu_id}
@endpoint_wrapper("/authentication/login") @endpoint_wrapper("/authentication/login")
@ -75,7 +81,7 @@ async def authentication_login_with_domain_and_creds(
""" """
Authenticate user with domain and credentials. Authenticate user with domain and credentials.
""" """
return AuthenticationLoginEventMethods.authentication_login_with_domain_and_creds( return await AuthenticationLoginEventMethods.authentication_login_with_domain_and_creds(
request=request, data=data request=request, data=data
) )
@ -83,13 +89,14 @@ async def authentication_login_with_domain_and_creds(
@endpoint_wrapper("/authentication/check") @endpoint_wrapper("/authentication/check")
async def authentication_check_token_is_valid( async def authentication_check_token_is_valid(
request: "Request", request: "Request",
data: DictRequestModel, data: EndpointBaseRequestModel,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Check if a token is valid. Check if a token is valid.
""" """
return { return {
"status": "OK", "headers": dict(request.headers),
"data": data.model_dump(),
} }
@ -97,8 +104,7 @@ async def authentication_check_token_is_valid(
@endpoint_wrapper("/authentication/refresh") @endpoint_wrapper("/authentication/refresh")
async def authentication_refresh_user_info( async def authentication_refresh_user_info(
request: "Request", request: "Request",
data: DictRequestModel, data: EndpointBaseRequestModel,
token_dict: TokenDictType = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Refresh user information. Refresh user information.
@ -111,8 +117,7 @@ async def authentication_refresh_user_info(
@endpoint_wrapper("/authentication/change-password") @endpoint_wrapper("/authentication/change-password")
async def authentication_change_password( async def authentication_change_password(
request: "Request", request: "Request",
data: ChangePasswordRequestModel, data: EndpointBaseRequestModel,
token_dict: TokenDictType = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Change user password. Change user password.
@ -125,7 +130,7 @@ async def authentication_change_password(
@endpoint_wrapper("/authentication/create-password") @endpoint_wrapper("/authentication/create-password")
async def authentication_create_password( async def authentication_create_password(
request: "Request", request: "Request",
data: CreatePasswordRequestModel, data: EndpointBaseRequestModel,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Create new password. Create new password.
@ -137,7 +142,7 @@ async def authentication_create_password(
@endpoint_wrapper("/authentication/forgot-password") @endpoint_wrapper("/authentication/forgot-password")
async def authentication_forgot_password( async def authentication_forgot_password(
request: "Request", request: "Request",
data: ForgotRequestModel, data: EndpointBaseRequestModel,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Handle forgot password request. Handle forgot password request.
@ -149,7 +154,7 @@ async def authentication_forgot_password(
@endpoint_wrapper("/authentication/reset-password") @endpoint_wrapper("/authentication/reset-password")
async def authentication_reset_password( async def authentication_reset_password(
request: "Request", request: "Request",
data: ForgotRequestModel, data: EndpointBaseRequestModel,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Reset password. Reset password.
@ -161,8 +166,7 @@ async def authentication_reset_password(
@endpoint_wrapper("/authentication/disconnect") @endpoint_wrapper("/authentication/disconnect")
async def authentication_disconnect_user( async def authentication_disconnect_user(
request: "Request", request: "Request",
data: LogoutRequestModel, data: EndpointBaseRequestModel,
token_dict: TokenDictType = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Disconnect user. Disconnect user.
@ -175,8 +179,7 @@ async def authentication_disconnect_user(
@endpoint_wrapper("/authentication/logout") @endpoint_wrapper("/authentication/logout")
async def authentication_logout_user( async def authentication_logout_user(
request: "Request", request: "Request",
data: LogoutRequestModel, data: EndpointBaseRequestModel,
token_dict: TokenDictType = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Logout user. Logout user.
@ -189,8 +192,7 @@ async def authentication_logout_user(
@endpoint_wrapper("/authentication/remember") @endpoint_wrapper("/authentication/remember")
async def authentication_refresher_token( async def authentication_refresher_token(
request: "Request", request: "Request",
data: RememberRequestModel, data: EndpointBaseRequestModel,
token_dict: TokenDictType = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Refresh remember token. Refresh remember token.
@ -203,8 +205,7 @@ async def authentication_refresher_token(
@endpoint_wrapper("/authentication/avatar") @endpoint_wrapper("/authentication/avatar")
async def authentication_download_avatar( async def authentication_download_avatar(
request: "Request", request: "Request",
data: DictRequestModel, data: EndpointBaseRequestModel,
token_dict: TokenDictType = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Download user avatar. Download user avatar.

View File

@ -127,12 +127,12 @@ class CreatePasswordRequestModel(BaseRequestModel[CreatePasswordData]):
) )
class SelectionDataOccupant(TypedDict): class SelectionDataOccupant(BaseModel):
"""Type for selection data.""" """Type for selection data."""
build_living_space_uu_id: Optional[str] build_living_space_uu_id: Optional[str]
class SelectionDataEmployee(TypedDict): class SelectionDataEmployee(BaseModel):
"""Type for selection data.""" """Type for selection data."""
company_uu_id: Optional[str] company_uu_id: Optional[str]

View File

@ -4,18 +4,16 @@ Account records endpoint configurations.
""" """
from ApiEvents.abstract_class import RouteFactoryConfig, EndpointFactoryConfig, endpoint_wrapper from ApiEvents.abstract_class import RouteFactoryConfig, EndpointFactoryConfig, endpoint_wrapper
from ApiEvents.base_request_model import DictRequestModel from ApiEvents.base_request_model import EndpointBaseRequestModel
from Services.PostgresDb.Models.alchemy_response import DictJsonResponse from Services.PostgresDb.Models.alchemy_response import DictJsonResponse
from fastapi import Request, Path, Body from fastapi import Request, Path, Body
from .models import ListOptionsRequestModel
@endpoint_wrapper("/account/records/address/list") @endpoint_wrapper("/account/records/address/list")
async def address_list(request: "Request", data: ListOptionsRequestModel): async def address_list(request: "Request", data: EndpointBaseRequestModel):
"""Handle address list endpoint.""" """Handle address list endpoint."""
auth_dict = address_list.auth
return { return {
"data": data, "data": data,
"request": str(request.headers), "request": str(request.headers),
@ -25,7 +23,7 @@ async def address_list(request: "Request", data: ListOptionsRequestModel):
@endpoint_wrapper("/account/records/address/create") @endpoint_wrapper("/account/records/address/create")
async def address_create(request: "Request", data: DictRequestModel): async def address_create(request: "Request", data: EndpointBaseRequestModel):
"""Handle address creation endpoint.""" """Handle address creation endpoint."""
return { return {
"data": data, "data": data,
@ -36,16 +34,18 @@ async def address_create(request: "Request", data: DictRequestModel):
@endpoint_wrapper("/account/records/address/search") @endpoint_wrapper("/account/records/address/search")
async def address_search(request: "Request", data: DictRequestModel): async def address_search(request: "Request", data: EndpointBaseRequestModel):
"""Handle address search endpoint.""" """Handle address search endpoint."""
return {"data": data} auth_dict = address_search.auth
code_dict = getattr(address_search, 'func_code', {"function_code": None})
return {"auth_dict": auth_dict, "code_dict": code_dict, "data": data}
@endpoint_wrapper("/account/records/address/{address_uu_id}") @endpoint_wrapper("/account/records/address/{address_uu_id}")
async def address_update( async def address_update(
request: Request, request: Request,
address_uu_id: str = Path(..., description="UUID of the address to update"), address_uu_id: str = Path(..., description="UUID of the address to update"),
request_data: DictRequestModel = Body(..., description="Request body"), request_data: EndpointBaseRequestModel = Body(..., description="Request body"),
): ):
""" """
Handle address update endpoint. Handle address update endpoint.
@ -58,6 +58,7 @@ async def address_update(
Returns: Returns:
DictJsonResponse: Response containing updated address info DictJsonResponse: Response containing updated address info
""" """
auth_dict = address_update.auth
return DictJsonResponse( return DictJsonResponse(
data={ data={
"address_uu_id": address_uu_id, "address_uu_id": address_uu_id,

View File

@ -49,15 +49,15 @@ def endpoint_wrapper(url_of_endpoint: Optional[str] = None):
# If result is a coroutine, await it # If result is a coroutine, await it
if inspect.iscoroutine(result): if inspect.iscoroutine(result):
result = await result result = await result
print('result', result)
# Add function_code to the result # Add endpoint to the result
if isinstance(result, dict): if isinstance(result, dict):
result["function_code"] = url_of_endpoint result["endpoint"] = url_of_endpoint
return result return result
elif isinstance(result, BaseModel): elif isinstance(result, BaseModel):
# Convert Pydantic model to dict and add function_code # Convert Pydantic model to dict and add endpoint
result_dict = result.model_dump() result_dict = result.model_dump()
result_dict["function_code"] = url_of_endpoint result_dict["endpoint"] = url_of_endpoint
return result_dict return result_dict
return result return result
@ -107,24 +107,23 @@ class EndpointFactoryConfig:
- If only event required -> wrap with EventMiddleware - If only event required -> wrap with EventMiddleware
- If only auth required -> wrap with MiddlewareModule.auth_required - If only auth required -> wrap with MiddlewareModule.auth_required
""" """
# Wrap the endpoint function to store url_of_endpoint # First apply auth/event middleware
self.endpoint_function = endpoint_wrapper(self.url_of_endpoint)( if self.is_event_required:
self.endpoint_function
)
if self.is_auth_required and self.is_event_required:
from middleware import TokenEventMiddleware from middleware import TokenEventMiddleware
self.endpoint_function = TokenEventMiddleware.event_required( self.endpoint_function = TokenEventMiddleware.event_required(
self.endpoint_function self.endpoint_function
) )
elif self.is_auth_required: elif self.is_auth_required:
from middleware import MiddlewareModule from middleware import MiddlewareModule
self.endpoint_function = MiddlewareModule.auth_required( self.endpoint_function = MiddlewareModule.auth_required(
self.endpoint_function self.endpoint_function
) )
# Then wrap with endpoint_wrapper to store url_of_endpoint
self.endpoint_function = endpoint_wrapper(self.url_of_endpoint)(
self.endpoint_function
)
class RouteFactoryConfig: class RouteFactoryConfig:
"""Configuration class for API route factories. """Configuration class for API route factories.

View File

@ -12,10 +12,21 @@ from pydantic import BaseModel, Field, ConfigDict, RootModel
T = TypeVar("T") T = TypeVar("T")
class EndpointBaseRequestModel(BaseModel):
data: dict = Field(..., description="Data to be sent with the request")
class Config:
json_schema_extra = {
"data": {
"key": "value",
}
}
class BaseRequestModel(RootModel[T], Generic[T]): class BaseRequestModel(RootModel[T], Generic[T]):
"""Base model for all API requests.""" """Base model for all API requests."""
model_config = ConfigDict( model_config = ConfigDict(
json_schema_extra={"example": {}} # Will be populated by subclasses json_schema_extra={"example": {"base": "example"}} # Will be populated by subclasses
) )

View File

@ -8,12 +8,14 @@ from ApiLibrary.common.line_number import get_line_number_for_error
from ApiLibrary.date_time_actions.date_functions import DateTimeLocal from ApiLibrary.date_time_actions.date_functions import DateTimeLocal
from ApiLibrary.token.password_module import PasswordModule from ApiLibrary.token.password_module import PasswordModule
from ErrorHandlers import HTTPExceptionApi from ErrorHandlers import HTTPExceptionApi
from Schemas.identity.identity import UsersTokens from Schemas.identity.identity import UsersTokens, People
from Services.Redis import RedisActions, AccessToken from Services.Redis import RedisActions, AccessToken
from ApiValidations.Custom.token_objects import ( from ApiValidations.Custom.token_objects import (
EmployeeTokenObject, EmployeeTokenObject,
OccupantTokenObject, OccupantTokenObject,
UserType, UserType,
CompanyToken,
OccupantToken,
) )
from Schemas import ( from Schemas import (
Users, Users,
@ -37,6 +39,7 @@ if TYPE_CHECKING:
T = TypeVar("T", EmployeeTokenObject, OccupantTokenObject) T = TypeVar("T", EmployeeTokenObject, OccupantTokenObject)
class TokenService: class TokenService:
"""Service class for handling authentication tokens and user sessions.""" """Service class for handling authentication tokens and user sessions."""
@ -128,22 +131,57 @@ class TokenService:
timezone=user.local_timezone or "GMT+0", timezone=user.local_timezone or "GMT+0",
lang=user.lang or "tr", lang=user.lang or "tr",
).model_dump() ).model_dump()
cls.set_object_to_redis(user, model_value) if access_token := cls.set_object_to_redis(user, model_value):
return { return {
"user_type": UserType.occupant.name, "access_token": access_token,
"available_occupants": occupants_selection_dict, "user_type": UserType.occupant.name,
} "available_occupants": occupants_selection_dict,
}
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg="Creating Token failed...",
)
@classmethod @classmethod
def set_object_to_redis(cls, user, model: Dict): def set_object_to_redis(cls, user, model: Dict):
accessObject = AccessToken( access_object = AccessToken(
userUUID=user.uu_id, userUUID=user.uu_id,
accessToken=cls._create_access_token(), accessToken=cls._create_access_token(),
) )
return RedisActions.set_json( redis_action = RedisActions.set_json(
list_keys=accessObject.to_list(), list_keys=access_object.to_list(),
value=json.dumps(model), value=model,
expires=Auth.TOKEN_EXPIRE_MINUTES_30.seconds, expires={"seconds": int(Auth.TOKEN_EXPIRE_MINUTES_30.seconds)},
)
if redis_action.status:
return access_object.accessToken
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg="Saving Token failed...",
)
@classmethod
def update_object_to_redis(cls, access_token: str, user_uu_id: str, model: Dict):
access_object = AccessToken(
userUUID=user_uu_id,
accessToken=access_token,
)
redis_action = RedisActions.set_json(
list_keys=access_object.to_list(),
value=model,
expires={"seconds": int(Auth.TOKEN_EXPIRE_MINUTES_30.seconds)},
)
if redis_action.status:
return access_object.accessToken
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg="Saving Token failed...",
) )
@classmethod @classmethod
@ -194,15 +232,17 @@ class TokenService:
"company_address": company_address, "company_address": company_address,
} }
) )
person = People.filter_one(
People.id == user.person_id, db=db_session
).data
model_value = EmployeeTokenObject( model_value = EmployeeTokenObject(
domain=domain, domain=domain,
user_type=UserType.employee.value, user_type=UserType.employee.value,
user_uu_id=str(user.uu_id), user_uu_id=str(user.uu_id),
credentials=user.credentials(), credentials=user.credentials(),
user_id=user.id, user_id=user.id,
person_id=user.person_id, person_id=person.id,
person_uu_id=str(user.person.uu_id), person_uu_id=str(person.uu_id),
request=dict(request.headers), request=dict(request.headers),
companies_uu_id_list=companies_uu_id_list, companies_uu_id_list=companies_uu_id_list,
companies_id_list=companies_id_list, companies_id_list=companies_id_list,
@ -211,8 +251,9 @@ class TokenService:
timezone=user.local_timezone or "GMT+0", timezone=user.local_timezone or "GMT+0",
lang=user.lang or "tr", lang=user.lang or "tr",
).model_dump() ).model_dump()
if cls.set_object_to_redis(user, model_value): if access_token := cls.set_object_to_redis(user, model_value):
return { return {
"access_token": access_token,
"user_type": UserType.employee.name, "user_type": UserType.employee.name,
"companies_list": companies_list, "companies_list": companies_list,
} }
@ -228,7 +269,8 @@ class TokenService:
"""Remove all tokens for a user with specific domain.""" """Remove all tokens for a user with specific domain."""
redis_rows = cls._get_user_tokens(user) redis_rows = cls._get_user_tokens(user)
for redis_row in redis_rows.all: for redis_row in redis_rows.all:
if redis_row.get("domain") == domain: print('redis_row', redis_row.data)
if redis_row.data.get("domain") == domain:
RedisActions.delete_key(redis_row.key) RedisActions.delete_key(redis_row.key)
@classmethod @classmethod
@ -291,6 +333,36 @@ class TokenService:
"user": user.get_dict(), "user": user.get_dict(),
} }
@classmethod
def update_token_at_redis(
cls, request: "Request", add_payload: Union[CompanyToken, OccupantToken]
) -> Dict[str, Any]:
"""Update token at Redis."""
access_token = cls.get_access_token_from_request(request=request)
token_object = cls.get_object_via_access_key(access_token=access_token)
if isinstance(token_object, EmployeeTokenObject) and isinstance(add_payload, CompanyToken):
token_object.selected_company = add_payload
cls.update_object_to_redis(
access_token=access_token,
user_uu_id=token_object.user_uu_id,
model=token_object.model_dump()
)
return token_object.selected_company.model_dump()
elif isinstance(token_object, OccupantTokenObject) and isinstance(add_payload, OccupantToken):
token_object.selected_occupant = add_payload
cls.update_object_to_redis(
access_token=access_token,
user_uu_id=token_object.user_uu_id,
model=token_object.model_dump()
)
return token_object.selected_occupant.model_dump()
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg="Token not found",
)
@classmethod @classmethod
def raise_error_if_request_has_no_token(cls, request: "Request") -> None: def raise_error_if_request_has_no_token(cls, request: "Request") -> None:
"""Validate request has required token headers.""" """Validate request has required token headers."""
@ -330,7 +402,6 @@ class TokenService:
redis_object["selected_company"] = None redis_object["selected_company"] = None
if not redis_object.get("selected_occupant"): if not redis_object.get("selected_occupant"):
redis_object["selected_occupant"] = None redis_object["selected_occupant"] = None
if redis_object.get("user_type") == UserType.employee.value: if redis_object.get("user_type") == UserType.employee.value:
return EmployeeTokenObject(**redis_object) return EmployeeTokenObject(**redis_object)
elif redis_object.get("user_type") == UserType.occupant.value: elif redis_object.get("user_type") == UserType.occupant.value:
@ -348,17 +419,17 @@ class TokenService:
"""Get token object using access key.""" """Get token object using access key."""
access_token_obj = AccessToken(accessToken=access_token) access_token_obj = AccessToken(accessToken=access_token)
redis_response = RedisActions.get_json(list_keys=access_token_obj.to_list()) redis_response = RedisActions.get_json(list_keys=access_token_obj.to_list())
if not redis_response.status:
if redis_object := redis_response.first.data: raise HTTPExceptionApi(
access_token_obj.userUUID = redis_object.get("user_uu_id") error_code="",
return cls._process_redis_object(redis_object) lang="en",
loc=get_line_number_for_error(),
raise HTTPExceptionApi( sys_msg="Access token token is not found or unable to retrieve",
error_code="", )
lang="en", if redis_object := redis_response.first:
loc=get_line_number_for_error(), redis_object_dict = redis_object.data
sys_msg="Invalid access token", access_token_obj.userUUID = redis_object_dict.get("user_uu_id")
) return cls._process_redis_object(redis_object_dict)
@classmethod @classmethod
def get_object_via_user_uu_id(cls, user_id: str) -> T: def get_object_via_user_uu_id(cls, user_id: str) -> T:

View File

@ -91,7 +91,15 @@ class OccupantTokenObject(ApplicationToken):
available_occupants: dict = None available_occupants: dict = None
selected_occupant: Optional[OccupantToken] = None # Selected Occupant Type selected_occupant: Optional[OccupantToken] = None # Selected Occupant Type
available_event: Optional[Any] = None
@property
def is_employee(self) -> bool:
return False
@property
def is_occupant(self) -> bool:
return True
class EmployeeTokenObject(ApplicationToken): class EmployeeTokenObject(ApplicationToken):
@ -104,4 +112,12 @@ class EmployeeTokenObject(ApplicationToken):
duty_uu_id_list: List[str] # List of duty objects duty_uu_id_list: List[str] # List of duty objects
selected_company: Optional[CompanyToken] = None # Selected Company Object selected_company: Optional[CompanyToken] = None # Selected Company Object
available_event: Optional[Any] = None
@property
def is_employee(self) -> bool:
return True
@property
def is_occupant(self) -> bool:
return False

View File

@ -1,3 +1,5 @@
from ApiValidations.Request import BaseModelRegular from ApiValidations.Request import BaseModelRegular
from typing import Optional from typing import Optional
@ -55,15 +57,11 @@ class OccupantSelectionValidation:
class OccupantSelection(BaseModel, OccupantSelectionValidation): class OccupantSelection(BaseModel, OccupantSelectionValidation):
occupant_uu_id: str = Field(..., example="123e4567-e89b-12d3-a456-426614174000") build_living_space_uu_id: str = Field(..., example="987fcdeb-51a2-43e7-9876-543210987654")
build_part_uu_id: str = Field(..., example="987fcdeb-51a2-43e7-9876-543210987654")
model_config = ConfigDict( model_config = ConfigDict(
json_schema_extra={ json_schema_extra={
"example": { "example": {"build_living_space_uu_id": "987fcdeb-51a2-43e7-9876-543210987654"}
"occupant_uu_id": "123e4567-e89b-12d3-a456-426614174000",
"build_part_uu_id": "987fcdeb-51a2-43e7-9876-543210987654",
}
} }
) )
@ -107,10 +105,10 @@ class Login(BaseModelRegular, LoginValidation):
model_config = ConfigDict( model_config = ConfigDict(
json_schema_extra={ json_schema_extra={
"example": { "example": {
"domain": "example.com", "domain": "evyos.com.tr",
"access_key": "user@example.com", "access_key": "karatay.berkay.sup@evyos.com.tr",
"password": "password123", "password": "string",
"remember_me": True, "remember_me": False
} }
} }
) )

View File

@ -10,77 +10,9 @@ This module initializes and configures the FastAPI application with:
""" """
import uvicorn import uvicorn
from fastapi import FastAPI
from create_routes import get_all_routers
from prometheus_fastapi_instrumentator import Instrumentator from prometheus_fastapi_instrumentator import Instrumentator
from app_handler import setup_middleware, get_uvicorn_config from app_handler import setup_middleware, get_uvicorn_config
from create_file import setup_security_schema, configure_route_security from create_file import create_app
from open_api_creator import OpenAPISchemaCreator, create_openapi_schema
def create_app() -> FastAPI:
"""Create and configure the FastAPI application."""
app = FastAPI(
responses={
422: {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"detail": {
"type": "array",
"items": {
"type": "object",
"properties": {
"loc": {
"type": "array",
"items": {"type": "string"},
},
"msg": {"type": "string"},
"type": {"type": "string"},
},
},
}
},
}
}
},
}
}
)
# Get all routers and protected routes from the new configuration
routers, protected_routes = get_all_routers()
# Include all routers
for router in routers:
app.include_router(router)
# Configure OpenAPI schema with security
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
# Create OpenAPI schema using our custom creator
openapi_schema = create_openapi_schema(app)
# Add security scheme
openapi_schema.update(setup_security_schema())
# Configure security for protected routes
for path, methods in protected_routes.items():
for method in methods:
configure_route_security(
path, method, openapi_schema, list(protected_routes.keys())
)
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
return app
app = create_app() # Initialize FastAPI application app = create_app() # Initialize FastAPI application

View File

@ -14,6 +14,7 @@ from fastapi import FastAPI, APIRouter
from fastapi.responses import JSONResponse, RedirectResponse from fastapi.responses import JSONResponse, RedirectResponse
from fastapi.openapi.utils import get_openapi from fastapi.openapi.utils import get_openapi
from AllConfigs.Token.config import Auth
from AllConfigs.main import MainConfig as Config from AllConfigs.main import MainConfig as Config
from create_routes import get_all_routers from create_routes import get_all_routers
@ -29,11 +30,11 @@ def setup_security_schema() -> Dict[str, Any]:
return { return {
"components": { "components": {
"securitySchemes": { "securitySchemes": {
"Bearer": { "Bearer Auth": {
"type": "http", "type": "apiKey",
"scheme": "bearer", "in": "header",
"bearerFormat": "JWT", "name": Auth.ACCESS_TOKEN_TAG,
"description": "Enter the token", "description": "Enter: **'Bearer <JWT>'**, where JWT is the access token",
} }
} }
} }
@ -65,12 +66,15 @@ def create_app() -> FastAPI:
Returns: Returns:
FastAPI: Configured FastAPI application instance FastAPI: Configured FastAPI application instance
""" """
# Initialize FastAPI app
from open_api_creator import create_openapi_schema
# Get all routers and protected routes using the dynamic route creation
app = FastAPI( app = FastAPI(
title=Config.TITLE, title=Config.TITLE,
description=Config.DESCRIPTION, description=Config.DESCRIPTION,
default_response_class=JSONResponse, default_response_class=JSONResponse,
) ) # Initialize FastAPI app
@app.get("/", include_in_schema=False, summary=str(Config.DESCRIPTION)) @app.get("/", include_in_schema=False, summary=str(Config.DESCRIPTION))
async def home() -> RedirectResponse: async def home() -> RedirectResponse:
@ -84,31 +88,5 @@ def create_app() -> FastAPI:
for router in routers: for router in routers:
app.include_router(router) app.include_router(router)
# Configure OpenAPI schema with security app.openapi = lambda app=app: create_openapi_schema(app)
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="WAG Management API",
version="4.0.0",
description="WAG Management API Service",
routes=app.routes,
)
# Add security scheme
security_schema = setup_security_schema()
openapi_schema.update(security_schema)
# Configure security for protected routes
for path, methods in protected_routes.items():
for method in methods:
configure_route_security(
path, method, openapi_schema, list(protected_routes.keys())
)
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
return app return app

View File

@ -24,8 +24,6 @@ class EndpointFactoryConfig:
summary: str summary: str
description: str description: str
endpoint_function: Callable[P, R] # Now accepts any parameters and return type endpoint_function: Callable[P, R] # Now accepts any parameters and return type
response_model: Optional[type] = None
request_model: Optional[type] = None
is_auth_required: bool = True is_auth_required: bool = True
is_event_required: bool = False is_event_required: bool = False
extra_options: Dict[str, Any] = None extra_options: Dict[str, Any] = None
@ -56,7 +54,7 @@ class EnhancedEndpointFactory:
endpoint_function = config.endpoint_function endpoint_function = config.endpoint_function
if config.is_auth_required: if config.is_auth_required:
endpoint_function = MiddlewareModule.auth_required(endpoint_function) # endpoint_function = MiddlewareModule.auth_required(endpoint_function)
# Track protected routes # Track protected routes
full_path = f"{self.router.prefix}{endpoint_path}" full_path = f"{self.router.prefix}{endpoint_path}"
if full_path not in self.protected_routes: if full_path not in self.protected_routes:
@ -66,7 +64,6 @@ class EnhancedEndpointFactory:
# Register the endpoint with FastAPI router # Register the endpoint with FastAPI router
getattr(self.router, config.method.lower())( getattr(self.router, config.method.lower())(
endpoint_path, endpoint_path,
response_model=config.response_model,
summary=config.summary, summary=config.summary,
description=config.description, description=config.description,
**config.extra_options, **config.extra_options,

View File

@ -15,6 +15,7 @@ from ApiLibrary.common.line_number import get_line_number_for_error
from ErrorHandlers.ErrorHandlers.api_exc_handler import HTTPExceptionApi from ErrorHandlers.ErrorHandlers.api_exc_handler import HTTPExceptionApi
from .base_context import BaseContext from .base_context import BaseContext
from ApiServices.Token.token_handler import OccupantTokenObject, EmployeeTokenObject from ApiServices.Token.token_handler import OccupantTokenObject, EmployeeTokenObject
import inspect
class AuthContext(BaseContext): class AuthContext(BaseContext):
@ -42,7 +43,12 @@ class AuthContext(BaseContext):
@property @property
def user_id(self) -> str: def user_id(self) -> str:
"""Get the user's UUID from token context.""" """Get the user's UUID from token context."""
return self.token_context.user_uu_id if self.token_context else "" return self.token_context.user_uu_id if self.token_context else None
def as_dict(self):
if not isinstance(self.token_context, dict):
return self.token_context.model_dump()
return self.token_context
def __repr__(self) -> str: def __repr__(self) -> str:
user_type = "Employee" if self.is_employee else "Occupant" user_type = "Employee" if self.is_employee else "Occupant"
@ -57,7 +63,7 @@ class MiddlewareModule:
@staticmethod @staticmethod
def get_user_from_request( def get_user_from_request(
request: Request, request: Request,
) -> AuthContext: ) -> dict:
""" """
Get authenticated token context from request. Get authenticated token context from request.
@ -74,7 +80,6 @@ class MiddlewareModule:
# Get token and validate - will raise HTTPExceptionApi if invalid # Get token and validate - will raise HTTPExceptionApi if invalid
redis_token = TokenService.get_access_token_from_request(request=request) redis_token = TokenService.get_access_token_from_request(request=request)
# Get token context - will validate token and raise appropriate errors # Get token context - will validate token and raise appropriate errors
token_context = TokenService.get_object_via_access_key(access_token=redis_token) token_context = TokenService.get_object_via_access_key(access_token=redis_token)
if not token_context: if not token_context:
@ -85,7 +90,7 @@ class MiddlewareModule:
sys_msg="TokenService: Token Context couldnt retrieved from redis", sys_msg="TokenService: Token Context couldnt retrieved from redis",
) )
return AuthContext(token_context=token_context) return token_context
@classmethod @classmethod
def auth_required(cls, func: Callable) -> Callable: def auth_required(cls, func: Callable) -> Callable:
@ -116,14 +121,13 @@ class MiddlewareModule:
""" """
@wraps(func) @wraps(func)
def wrapper(request: Request, *args, **kwargs): async def wrapper(request: Request, *args, **kwargs):
# Get and validate token context from request # Get and validate token context from request
auth_context = cls.get_user_from_request(request) # Create auth context and Attach auth context to both wrapper and original function
func.auth = cls.get_user_from_request(request) # This ensures the context is available in both places
# Attach auth context to function
func.auth = auth_context
# Call the original endpoint function # Call the original endpoint function
if inspect.iscoroutinefunction(func):
return await func(request, *args, **kwargs)
return func(request, *args, **kwargs) return func(request, *args, **kwargs)
return wrapper return wrapper
@ -147,7 +151,6 @@ class RequestTimingMiddleware(BaseHTTPMiddleware):
Response: Processed response with timing headers Response: Processed response with timing headers
""" """
start_time = perf_counter() start_time = perf_counter()
# Process the request # Process the request
response = await call_next(request) response = await call_next(request)

View File

@ -6,6 +6,7 @@ from functools import wraps
from typing import Callable, Dict, Any from typing import Callable, Dict, Any
from .auth_middleware import MiddlewareModule from .auth_middleware import MiddlewareModule
from .base_context import BaseContext from .base_context import BaseContext
import inspect
class TokenEventHandler(BaseContext): class TokenEventHandler(BaseContext):
@ -51,26 +52,14 @@ class TokenEventMiddleware:
authenticated_func = MiddlewareModule.auth_required(func) authenticated_func = MiddlewareModule.auth_required(func)
@wraps(authenticated_func) @wraps(authenticated_func)
def wrapper(*args, **kwargs) -> Dict[str, Any]: async def wrapper(*args, **kwargs) -> Dict[str, Any]:
# Create handler with context # Create handler with context
handler = TokenEventHandler( function_code = "7192c2aa-5352-4e36-98b3-dafb7d036a3d" # Keep function_code as URL
func=authenticated_func,
url_of_endpoint=authenticated_func.url_of_endpoint,
)
# Update event-specific context
handler.update_context(
function_code="7192c2aa-5352-4e36-98b3-dafb7d036a3d" # Keep function_code as URL
)
# Copy auth context from authenticated function
if hasattr(authenticated_func, "auth"):
handler.token_context = authenticated_func.auth.token_context
# Make handler available to the function
authenticated_func.handler = handler
# Make handler available to all functions in the chain
func.func_code = {"function_code": function_code}
# Call the authenticated function # Call the authenticated function
if inspect.iscoroutinefunction(authenticated_func):
return await authenticated_func(*args, **kwargs)
return authenticated_func(*args, **kwargs) return authenticated_func(*args, **kwargs)
return wrapper return wrapper

View File

@ -13,6 +13,8 @@ from typing import Any, Dict, List, Optional, Set
from fastapi import FastAPI, APIRouter from fastapi import FastAPI, APIRouter
from fastapi.routing import APIRoute from fastapi.routing import APIRoute
from fastapi.openapi.utils import get_openapi from fastapi.openapi.utils import get_openapi
from AllConfigs.Token.config import Auth
from AllConfigs.main import MainConfig as Config from AllConfigs.main import MainConfig as Config
from create_routes import get_all_routers from create_routes import get_all_routers
@ -62,17 +64,11 @@ class OpenAPISchemaCreator:
""" """
return { return {
"Bearer Auth": { "Bearer Auth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "Enter the token with the `Bearer: ` prefix",
},
"API Key": {
"type": "apiKey", "type": "apiKey",
"in": "header", "in": "header",
"name": "X-API-Key", "name": Auth.ACCESS_TOKEN_TAG,
"description": "Optional API key for service authentication", "description": "Enter: **'Bearer <JWT>'**, where JWT is the access token",
}, }
} }
def _create_common_responses(self) -> Dict[str, Any]: def _create_common_responses(self) -> Dict[str, Any]:
@ -198,7 +194,6 @@ class OpenAPISchemaCreator:
if path in self.protected_routes and method in self.protected_routes[path]: if path in self.protected_routes and method in self.protected_routes[path]:
schema["paths"][path][method]["security"] = [ schema["paths"][path][method]["security"] = [
{"Bearer Auth": []}, {"Bearer Auth": []},
{"API Key": []},
] ]
schema["paths"][path][method]["responses"].update( schema["paths"][path][method]["responses"].update(
self._create_common_responses() self._create_common_responses()
@ -219,7 +214,7 @@ class OpenAPISchemaCreator:
openapi_schema = get_openapi( openapi_schema = get_openapi(
title=Config.TITLE, title=Config.TITLE,
description=Config.DESCRIPTION, description=Config.DESCRIPTION,
version="1.0.0", version="1.1.1",
routes=self.app.routes, routes=self.app.routes,
tags=self.tags_metadata, tags=self.tags_metadata,
) )
@ -229,17 +224,15 @@ class OpenAPISchemaCreator:
openapi_schema["components"] = {} openapi_schema["components"] = {}
openapi_schema["components"]["securitySchemes"] = self._create_security_schemes() openapi_schema["components"]["securitySchemes"] = self._create_security_schemes()
# Configure route security and responses # Configure route security and responses
for route in self.app.routes: for route in self.app.routes:
if isinstance(route, APIRoute) and route.include_in_schema: if isinstance(route, APIRoute) and route.include_in_schema:
path = str(route.path) path = str(route.path)
methods = [method.lower() for method in route.methods] methods = [method.lower() for method in route.methods]
for method in methods: for method in methods:
self.configure_route_security(path, method, openapi_schema) self.configure_route_security(path, method, openapi_schema)
# Add custom documentation extensions # # Add custom documentation extensions
openapi_schema["x-documentation"] = { openapi_schema["x-documentation"] = {
"postman_collection": "/docs/postman", "postman_collection": "/docs/postman",
"swagger_ui": "/docs", "swagger_ui": "/docs",

View File

@ -251,18 +251,22 @@ class Event2Employee(CrudCollection):
@classmethod @classmethod
def get_event_id_by_employee_id(cls, employee_id) -> list: def get_event_id_by_employee_id(cls, employee_id) -> list:
db = cls.new_session()
occupant_events = cls.filter_all( occupant_events = cls.filter_all(
cls.employee_id == employee_id, cls.employee_id == employee_id,
db=db,
).data ).data
active_events = Service2Events.filter_all( active_events = Service2Events.filter_all(
Service2Events.service_id.in_( Service2Events.service_id.in_(
[event.event_service_id for event in occupant_events] [event.event_service_id for event in occupant_events]
), ),
db=db,
system=True, system=True,
).data ).data
active_events_id = [event.event_id for event in active_events] active_events_id = [event.event_id for event in active_events]
if extra_events := Event2EmployeeExtra.filter_all( if extra_events := Event2EmployeeExtra.filter_all(
Event2EmployeeExtra.employee_id == employee_id Event2EmployeeExtra.employee_id == employee_id,
db=db,
).data: ).data:
active_events_id.extend([event.event_id for event in extra_events]) active_events_id.extend([event.event_id for event in extra_events])
return active_events_id return active_events_id

View File

@ -77,12 +77,7 @@ class RedisActions:
time=expiry_time, time=expiry_time,
value=redis_row.value, value=redis_row.value,
) )
redis_row.expires_at = ( redis_row.expires_at = str(arrow.now().shift(seconds=expiry_time).format(MainConfig.DATETIME_FORMAT))
arrow.now()
.shift(seconds=expiry_time)
.format(MainConfig.DATETIME_FORMAT)
)
else: else:
redis_cli.set(name=redis_row.redis_key, value=redis_row.value) redis_cli.set(name=redis_row.redis_key, value=redis_row.value)
@ -115,7 +110,6 @@ class RedisActions:
list_of_rows = [] list_of_rows = []
regex = RedisRow.regex(list_keys=list_keys) regex = RedisRow.regex(list_keys=list_keys)
json_get = redis_cli.scan_iter(match=regex) json_get = redis_cli.scan_iter(match=regex)
for row in list(json_get): for row in list(json_get):
redis_row = RedisRow() redis_row = RedisRow()
redis_row.set_key(key=row) redis_row.set_key(key=row)
@ -123,11 +117,16 @@ class RedisActions:
redis_value = redis_cli.get(redis_row.redis_key) redis_value = redis_cli.get(redis_row.redis_key)
redis_row.feed(redis_value) redis_row.feed(redis_value)
list_of_rows.append(redis_row) list_of_rows.append(redis_row)
if list_of_rows:
return RedisResponse(
status=True,
message="Value is get successfully.",
data=list_of_rows,
)
return RedisResponse( return RedisResponse(
status=True, status=False,
message="Value is get successfully.", message="Value is not get successfully.",
data=list_of_rows, data=list_of_rows
) )
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(
@ -135,3 +134,4 @@ class RedisActions:
message="Value is not get successfully.", message="Value is not get successfully.",
error=str(e), error=str(e),
) )

View File

@ -94,7 +94,7 @@ class RedisRow:
# Filter and convert valid keys # Filter and convert valid keys
valid_keys = [] valid_keys = []
for key in list_keys: for key in list_keys:
if key is None: if key is None or str(key) == "None":
continue continue
if isinstance(key, bytes): if isinstance(key, bytes):
key = key.decode() key = key.decode()
@ -108,7 +108,8 @@ class RedisRow:
# Add wildcard if first key was None # Add wildcard if first key was None
if list_keys[0] is None: if list_keys[0] is None:
pattern = f"*{cls.delimiter}{pattern}" pattern = f"*{cls.delimiter}{pattern}"
if '*' not in pattern:
pattern = f"{pattern}:*"
return pattern return pattern
@classmethod @classmethod

View File

@ -47,4 +47,7 @@ class RedisResponse:
@property @property
def first(self) -> Union[RedisRow, None]: def first(self) -> Union[RedisRow, None]:
return self.data[0] if self.data else None if self.data:
return self.data[0]
self.status = False
return

View File

@ -1,23 +1,23 @@
from typing import Optional from typing import Optional, Literal
from uuid import UUID from uuid import UUID
from pydantic import BaseModel, validator from pydantic import BaseModel, field_validator
class AccessToken(BaseModel): class AccessToken(BaseModel):
accessToken: Optional[str] = None accessToken: Optional[str] = None
userUUID: Optional[str] = None userUUID: Optional[str | UUID] = None
@validator("userUUID", pre=True) @field_validator("userUUID", mode="after")
def validate_uuid(cls, v): def validate_uuid(cls, v):
"""Convert UUID to string during validation.""" """Convert UUID to string during validation."""
if isinstance(v, UUID): if v is None:
return str(v) return None
return v return str(v)
def to_list(self): def to_list(self):
"""Convert to list for Redis storage.""" """Convert to list for Redis storage."""
return [self.accessToken, self.userUUID] return [self.accessToken, str(self.userUUID) if self.userUUID else None]
@property @property
def count(self): def count(self):