base context for wrappers updated
This commit is contained in:
@@ -8,10 +8,22 @@ from typing import TYPE_CHECKING, Union, Dict, Any
|
||||
from ApiEvents.abstract_class import MethodToEvent
|
||||
from ApiEvents.base_request_model import DictRequestModel, SuccessResponse
|
||||
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.Token.token_handler import AccessToken, TokenService
|
||||
from ApiValidations.Request.authentication import EmployeeSelectionValidation, Login, OccupantSelectionValidation
|
||||
from ApiServices.Token.token_handler import TokenService
|
||||
from ApiValidations.Custom.token_objects import CompanyToken
|
||||
from ApiValidations.Request.authentication import (
|
||||
Login,
|
||||
EmployeeSelectionValidation,
|
||||
OccupantSelectionValidation, OccupantSelection, EmployeeSelection,
|
||||
)
|
||||
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 (
|
||||
LoginData,
|
||||
LoginRequestModel,
|
||||
@@ -27,7 +39,10 @@ from .models import (
|
||||
|
||||
if TYPE_CHECKING:
|
||||
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
|
||||
TokenDictType = Union["EmployeeTokenObject", "OccupantTokenObject"]
|
||||
@@ -69,7 +84,7 @@ class AuthenticationLoginEventMethods(MethodToEvent):
|
||||
|
||||
# Return response with token and headers
|
||||
return {
|
||||
"token": token,
|
||||
**token,
|
||||
"headers": dict(request.headers),
|
||||
}
|
||||
|
||||
@@ -89,18 +104,113 @@ class AuthenticationSelectEventMethods(MethodToEvent):
|
||||
@classmethod
|
||||
def _handle_employee_selection(
|
||||
cls,
|
||||
data: SelectionDataEmployee,
|
||||
data: EmployeeSelection,
|
||||
token_dict: TokenDictType,
|
||||
request: "Request",
|
||||
):
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
Users.set_user_define_properties(token=token_dict)
|
||||
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
|
||||
def _handle_occupant_selection(
|
||||
cls,
|
||||
data: SelectionDataOccupant,
|
||||
data: OccupantSelection,
|
||||
token_dict: TokenDictType,
|
||||
request: "Request",
|
||||
):
|
||||
@@ -116,25 +226,22 @@ class AuthenticationSelectEventMethods(MethodToEvent):
|
||||
async def authentication_select_company_or_occupant_type(
|
||||
cls,
|
||||
request: "Request",
|
||||
data: Union[EmployeeSelectionValidation, OccupantSelectionValidation],
|
||||
data: Union[EmployeeSelection, OccupantSelection],
|
||||
token_dict: TokenDictType,
|
||||
):
|
||||
"""Handle selection of company or occupant type"""
|
||||
try:
|
||||
print(
|
||||
dict(
|
||||
data=data,
|
||||
token_dict=token_dict.model_dump(),
|
||||
request=dict(request.headers)
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPExceptionApi(
|
||||
error_code="HTTP_500_INTERNAL_SERVER_ERROR",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
if token_dict.is_employee:
|
||||
return cls._handle_employee_selection(data, token_dict, request)
|
||||
elif token_dict.is_occupant:
|
||||
return cls._handle_occupant_selection(data, token_dict, request)
|
||||
|
||||
# except Exception as e:
|
||||
# raise HTTPExceptionApi(
|
||||
# error_code="HTTP_500_INTERNAL_SERVER_ERROR",
|
||||
# lang="en",
|
||||
# loc=get_line_number_for_error(),
|
||||
# sys_msg=str(e),
|
||||
# )
|
||||
|
||||
|
||||
class AuthenticationCheckTokenEventMethods(MethodToEvent):
|
||||
|
||||
@@ -5,7 +5,16 @@ Authentication endpoint configurations.
|
||||
from typing import TYPE_CHECKING, Dict, Any, Union, Annotated
|
||||
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 (
|
||||
AuthenticationChangePasswordEventMethods,
|
||||
@@ -32,7 +41,7 @@ from .models import (
|
||||
SelectionDataOccupant,
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -41,30 +50,27 @@ from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTok
|
||||
|
||||
|
||||
# Type aliases for common types
|
||||
TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject]
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/select")
|
||||
async def authentication_select_company_or_occupant_type(
|
||||
request: "Request",
|
||||
data: Union[SelectionDataEmployee, SelectionDataOccupant],
|
||||
token_dict: TokenDictType = None
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Handle selection of company or occupant type.
|
||||
|
||||
Args:
|
||||
request: The FastAPI request object
|
||||
data: Selection request data
|
||||
|
||||
Returns:
|
||||
Dict containing the response data
|
||||
Select company or occupant type.
|
||||
"""
|
||||
return {
|
||||
"headers": dict(request.headers),
|
||||
"data": data,
|
||||
"token": token_dict
|
||||
}
|
||||
auth_dict = authentication_select_company_or_occupant_type.auth
|
||||
if data.data.get("company_uu_id"):
|
||||
data = EmployeeSelection(**data.data)
|
||||
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")
|
||||
@@ -75,7 +81,7 @@ async def authentication_login_with_domain_and_creds(
|
||||
"""
|
||||
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
|
||||
)
|
||||
|
||||
@@ -83,13 +89,14 @@ async def authentication_login_with_domain_and_creds(
|
||||
@endpoint_wrapper("/authentication/check")
|
||||
async def authentication_check_token_is_valid(
|
||||
request: "Request",
|
||||
data: DictRequestModel,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Check if a token is valid.
|
||||
"""
|
||||
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")
|
||||
async def authentication_refresh_user_info(
|
||||
request: "Request",
|
||||
data: DictRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Refresh user information.
|
||||
@@ -111,8 +117,7 @@ async def authentication_refresh_user_info(
|
||||
@endpoint_wrapper("/authentication/change-password")
|
||||
async def authentication_change_password(
|
||||
request: "Request",
|
||||
data: ChangePasswordRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Change user password.
|
||||
@@ -125,7 +130,7 @@ async def authentication_change_password(
|
||||
@endpoint_wrapper("/authentication/create-password")
|
||||
async def authentication_create_password(
|
||||
request: "Request",
|
||||
data: CreatePasswordRequestModel,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Create new password.
|
||||
@@ -137,7 +142,7 @@ async def authentication_create_password(
|
||||
@endpoint_wrapper("/authentication/forgot-password")
|
||||
async def authentication_forgot_password(
|
||||
request: "Request",
|
||||
data: ForgotRequestModel,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Handle forgot password request.
|
||||
@@ -149,7 +154,7 @@ async def authentication_forgot_password(
|
||||
@endpoint_wrapper("/authentication/reset-password")
|
||||
async def authentication_reset_password(
|
||||
request: "Request",
|
||||
data: ForgotRequestModel,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Reset password.
|
||||
@@ -161,8 +166,7 @@ async def authentication_reset_password(
|
||||
@endpoint_wrapper("/authentication/disconnect")
|
||||
async def authentication_disconnect_user(
|
||||
request: "Request",
|
||||
data: LogoutRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Disconnect user.
|
||||
@@ -175,8 +179,7 @@ async def authentication_disconnect_user(
|
||||
@endpoint_wrapper("/authentication/logout")
|
||||
async def authentication_logout_user(
|
||||
request: "Request",
|
||||
data: LogoutRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Logout user.
|
||||
@@ -189,8 +192,7 @@ async def authentication_logout_user(
|
||||
@endpoint_wrapper("/authentication/remember")
|
||||
async def authentication_refresher_token(
|
||||
request: "Request",
|
||||
data: RememberRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Refresh remember token.
|
||||
@@ -203,8 +205,7 @@ async def authentication_refresher_token(
|
||||
@endpoint_wrapper("/authentication/avatar")
|
||||
async def authentication_download_avatar(
|
||||
request: "Request",
|
||||
data: DictRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
data: EndpointBaseRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Download user avatar.
|
||||
|
||||
@@ -127,12 +127,12 @@ class CreatePasswordRequestModel(BaseRequestModel[CreatePasswordData]):
|
||||
)
|
||||
|
||||
|
||||
class SelectionDataOccupant(TypedDict):
|
||||
class SelectionDataOccupant(BaseModel):
|
||||
"""Type for selection data."""
|
||||
build_living_space_uu_id: Optional[str]
|
||||
|
||||
|
||||
class SelectionDataEmployee(TypedDict):
|
||||
class SelectionDataEmployee(BaseModel):
|
||||
"""Type for selection data."""
|
||||
company_uu_id: Optional[str]
|
||||
|
||||
|
||||
@@ -4,18 +4,16 @@ Account records endpoint configurations.
|
||||
"""
|
||||
|
||||
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 fastapi import Request, Path, Body
|
||||
|
||||
from .models import ListOptionsRequestModel
|
||||
|
||||
|
||||
@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."""
|
||||
auth_dict = address_list.auth
|
||||
return {
|
||||
"data": data,
|
||||
"request": str(request.headers),
|
||||
@@ -25,7 +23,7 @@ async def address_list(request: "Request", data: ListOptionsRequestModel):
|
||||
|
||||
|
||||
@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."""
|
||||
return {
|
||||
"data": data,
|
||||
@@ -36,16 +34,18 @@ async def address_create(request: "Request", data: DictRequestModel):
|
||||
|
||||
|
||||
@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."""
|
||||
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}")
|
||||
async def address_update(
|
||||
request: Request,
|
||||
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.
|
||||
@@ -58,6 +58,7 @@ async def address_update(
|
||||
Returns:
|
||||
DictJsonResponse: Response containing updated address info
|
||||
"""
|
||||
auth_dict = address_update.auth
|
||||
return DictJsonResponse(
|
||||
data={
|
||||
"address_uu_id": address_uu_id,
|
||||
|
||||
@@ -49,15 +49,15 @@ def endpoint_wrapper(url_of_endpoint: Optional[str] = None):
|
||||
# If result is a coroutine, await it
|
||||
if inspect.iscoroutine(result):
|
||||
result = await result
|
||||
|
||||
# Add function_code to the result
|
||||
print('result', result)
|
||||
# Add endpoint to the result
|
||||
if isinstance(result, dict):
|
||||
result["function_code"] = url_of_endpoint
|
||||
result["endpoint"] = url_of_endpoint
|
||||
return result
|
||||
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["function_code"] = url_of_endpoint
|
||||
result_dict["endpoint"] = url_of_endpoint
|
||||
return result_dict
|
||||
return result
|
||||
|
||||
@@ -107,24 +107,23 @@ class EndpointFactoryConfig:
|
||||
- If only event required -> wrap with EventMiddleware
|
||||
- If only auth required -> wrap with MiddlewareModule.auth_required
|
||||
"""
|
||||
# Wrap the endpoint function to store url_of_endpoint
|
||||
self.endpoint_function = endpoint_wrapper(self.url_of_endpoint)(
|
||||
self.endpoint_function
|
||||
)
|
||||
|
||||
if self.is_auth_required and self.is_event_required:
|
||||
# First apply auth/event middleware
|
||||
if self.is_event_required:
|
||||
from middleware import TokenEventMiddleware
|
||||
|
||||
self.endpoint_function = TokenEventMiddleware.event_required(
|
||||
self.endpoint_function
|
||||
)
|
||||
elif self.is_auth_required:
|
||||
from middleware import MiddlewareModule
|
||||
|
||||
self.endpoint_function = MiddlewareModule.auth_required(
|
||||
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:
|
||||
"""Configuration class for API route factories.
|
||||
|
||||
@@ -12,10 +12,21 @@ from pydantic import BaseModel, Field, ConfigDict, RootModel
|
||||
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]):
|
||||
"""Base model for all API requests."""
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={"example": {}} # Will be populated by subclasses
|
||||
json_schema_extra={"example": {"base": "example"}} # Will be populated by subclasses
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user