events auth repair
This commit is contained in:
parent
426b69b33c
commit
61229cb761
|
|
@ -0,0 +1,9 @@
|
|||
"""
|
||||
Authentication package initialization.
|
||||
"""
|
||||
|
||||
from .endpoints import AUTH_CONFIG
|
||||
|
||||
__all__ = [
|
||||
"AUTH_CONFIG",
|
||||
]
|
||||
|
|
@ -2,103 +2,37 @@
|
|||
Authentication related API endpoints.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Union, Optional, Dict, Any
|
||||
from typing import TYPE_CHECKING, Union, Dict, Any
|
||||
|
||||
# Regular imports (non-TYPE_CHECKING)
|
||||
from ApiEvents.abstract_class import (
|
||||
MethodToEvent,
|
||||
RouteFactoryConfig,
|
||||
EndpointFactoryConfig,
|
||||
)
|
||||
from ApiEvents.base_request_model import (
|
||||
BaseRequestModel,
|
||||
DictRequestModel,
|
||||
SuccessResponse,
|
||||
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 ApiServices.Login.user_login_handler import UserLoginModule
|
||||
from ApiServices.Token.token_handler import AccessToken, TokenService
|
||||
from ApiValidations.Request.authentication import EmployeeSelectionValidation, Login, OccupantSelectionValidation
|
||||
from ErrorHandlers import HTTPExceptionApi
|
||||
from .models import (
|
||||
LoginData,
|
||||
LoginRequestModel,
|
||||
LogoutRequestModel,
|
||||
RememberRequestModel,
|
||||
ForgotRequestModel,
|
||||
ChangePasswordRequestModel,
|
||||
CreatePasswordRequestModel,
|
||||
SelectionDataEmployee,
|
||||
SelectionDataOccupant,
|
||||
)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastapi import Request
|
||||
from ApiServices.Token.token_handler import OccupantTokenObject, EmployeeTokenObject
|
||||
from Schemas import (
|
||||
Login,
|
||||
ChangePassword,
|
||||
CreatePassword,
|
||||
Forgot,
|
||||
Logout,
|
||||
Remember,
|
||||
EmployeeSelection,
|
||||
OccupantSelection,
|
||||
)
|
||||
|
||||
from ApiLibrary.common.line_number import get_line_number_for_error
|
||||
from ApiServices.Login.user_login_handler import UserLoginModule
|
||||
from ApiValidations.Request import (
|
||||
Login,
|
||||
Logout,
|
||||
Remember,
|
||||
Forgot,
|
||||
ChangePassword,
|
||||
CreatePassword,
|
||||
EmployeeSelection,
|
||||
OccupantSelection,
|
||||
)
|
||||
from Services.PostgresDb.Models.alchemy_response import AlchemyJsonResponse
|
||||
from ApiValidations.Response import AccountRecordResponse
|
||||
from ApiServices.Token.token_handler import AccessToken, TokenService
|
||||
from ErrorHandlers import HTTPExceptionApi
|
||||
|
||||
# Type aliases for common types
|
||||
TokenDictType = Union["EmployeeTokenObject", "OccupantTokenObject"]
|
||||
|
||||
|
||||
class LoginRequestModel(BaseRequestModel[Login]):
|
||||
"""Request model for login endpoint."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LogoutRequestModel(BaseRequestModel[Logout]):
|
||||
"""Request model for logout endpoint."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class RememberRequestModel(BaseRequestModel[Remember]):
|
||||
"""Request model for remember token endpoint."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ForgotRequestModel(BaseRequestModel[Forgot]):
|
||||
"""Request model for forgot password endpoint."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ChangePasswordRequestModel(BaseRequestModel[ChangePassword]):
|
||||
"""Request model for change password endpoint."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CreatePasswordRequestModel(BaseRequestModel[CreatePassword]):
|
||||
"""Request model for create password endpoint."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class EmployeeSelectionRequestModel(BaseRequestModel[EmployeeSelection]):
|
||||
"""Request model for employee selection endpoint."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class OccupantSelectionRequestModel(BaseRequestModel[OccupantSelection]):
|
||||
"""Request model for occupant selection endpoint."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AuthenticationLoginEventMethods(MethodToEvent):
|
||||
event_type = "LOGIN"
|
||||
event_description = "Login via domain and access key : [email] | [phone]"
|
||||
|
|
@ -112,7 +46,7 @@ class AuthenticationLoginEventMethods(MethodToEvent):
|
|||
|
||||
@classmethod
|
||||
async def authentication_login_with_domain_and_creds(
|
||||
cls, request: "Request", data: LoginRequestModel
|
||||
cls, request: "Request", data: Login
|
||||
):
|
||||
"""
|
||||
Authenticate user with domain and credentials.
|
||||
|
|
@ -129,11 +63,9 @@ class AuthenticationLoginEventMethods(MethodToEvent):
|
|||
Returns:
|
||||
SuccessResponse containing authentication token and user info
|
||||
"""
|
||||
# Create login module instance
|
||||
login_module = UserLoginModule(request=request)
|
||||
|
||||
# Get token from login module
|
||||
token = await login_module.login_user_via_credentials(access_data=data)
|
||||
user_login_module = UserLoginModule(request=request)
|
||||
token = await user_login_module.login_user_via_credentials(access_data=data)
|
||||
|
||||
# Return response with token and headers
|
||||
return {
|
||||
|
|
@ -157,135 +89,53 @@ class AuthenticationSelectEventMethods(MethodToEvent):
|
|||
@classmethod
|
||||
def _handle_employee_selection(
|
||||
cls,
|
||||
data: EmployeeSelectionRequestModel,
|
||||
data: SelectionDataEmployee,
|
||||
token_dict: TokenDictType,
|
||||
request: "Request",
|
||||
):
|
||||
return
|
||||
# """Handle employee company selection"""
|
||||
# Users.client_arrow = DateTimeLocal(is_client=True, timezone=token_dict.timezone)
|
||||
# if data.company_uu_id not in token_dict.companies_uu_id_list:
|
||||
# return ResponseHandler.unauthorized(
|
||||
# "Company not found in user's company list"
|
||||
# )
|
||||
|
||||
# selected_company = Companies.filter_one(
|
||||
# Companies.uu_id == data.company_uu_id
|
||||
# ).data
|
||||
# if not selected_company:
|
||||
# return ResponseHandler.not_found("Company not found")
|
||||
|
||||
# # Get department IDs for the company
|
||||
# department_ids = [
|
||||
# dept.id
|
||||
# for dept in Departments.filter_all(
|
||||
# Departments.company_id == selected_company.id
|
||||
# ).data
|
||||
# ]
|
||||
|
||||
# # Get duties IDs for the company
|
||||
# duties_ids = [
|
||||
# duty.id
|
||||
# for duty in Duties.filter_all(Duties.company_id == selected_company.id).data
|
||||
# ]
|
||||
|
||||
# # Get staff IDs
|
||||
# staff_ids = [
|
||||
# staff.id for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids)).data
|
||||
# ]
|
||||
|
||||
# # Get employee
|
||||
# employee = Employees.filter_one(
|
||||
# Employees.people_id == token_dict.person_id,
|
||||
# Employees.staff_id.in_(staff_ids),
|
||||
# ).data
|
||||
|
||||
# if not employee:
|
||||
# return ResponseHandler.not_found("Employee not found")
|
||||
|
||||
# # 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).data
|
||||
# duties = Duties.filter_one(Duties.id == staff.duties_id).data
|
||||
# department = Departments.filter_one(Departments.id == duties.department_id).data
|
||||
|
||||
# # Get bulk duty
|
||||
# bulk_id = Duty.filter_by_one(system=True, duty_code="BULK").data
|
||||
# bulk_duty_id = Duties.filter_by_one(
|
||||
# company_id=selected_company.id,
|
||||
# duties_id=bulk_id.id,
|
||||
# **Duties.valid_record_dict,
|
||||
# ).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,
|
||||
# )
|
||||
|
||||
# # Update Redis
|
||||
# AuthActions.update_selected_to_redis(request=request, add_payload=company_token)
|
||||
# return ResponseHandler.success("Company selected successfully")
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _handle_occupant_selection(
|
||||
cls,
|
||||
data: OccupantSelectionRequestModel,
|
||||
data: SelectionDataOccupant,
|
||||
token_dict: TokenDictType,
|
||||
request: "Request",
|
||||
):
|
||||
"""Handle occupant type selection"""
|
||||
raise HTTPExceptionApi(
|
||||
error_code="HTTP_400_BAD_REQUEST",
|
||||
lang=token_dict.lang,
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="Occupant selection not implemented",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def authentication_select_company_or_occupant_type(
|
||||
cls,
|
||||
request: "Request",
|
||||
data: Union[EmployeeSelectionValidation, OccupantSelectionValidation],
|
||||
token_dict: TokenDictType,
|
||||
):
|
||||
"""Handle selection of company or occupant type"""
|
||||
try:
|
||||
if isinstance(token_dict, EmployeeTokenObject):
|
||||
return cls._handle_employee_selection(data, token_dict, request)
|
||||
elif isinstance(token_dict, OccupantTokenObject):
|
||||
return cls._handle_occupant_selection(data, token_dict, request)
|
||||
raise HTTPExceptionApi(
|
||||
error_code="HTTP_400_BAD_REQUEST",
|
||||
lang=token_dict.lang,
|
||||
loc=get_line_number_for_error(),
|
||||
print(
|
||||
dict(
|
||||
data=data,
|
||||
token_dict=token_dict.model_dump(),
|
||||
request=dict(request.headers)
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return ResponseHandler.error(
|
||||
str(e), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
raise HTTPExceptionApi(
|
||||
error_code="HTTP_500_INTERNAL_SERVER_ERROR",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def authentication_select_company_or_occupant_type(
|
||||
cls,
|
||||
request: "Request",
|
||||
data: Union[EmployeeSelectionRequestModel, OccupantSelectionRequestModel],
|
||||
token_dict: TokenDictType,
|
||||
):
|
||||
"""Handle selection of company or occupant type"""
|
||||
# try:
|
||||
# if isinstance(token_dict, EmployeeTokenObject):
|
||||
# return cls._handle_employee_selection(data, token_dict, request)
|
||||
# elif isinstance(token_dict, OccupantTokenObject):
|
||||
# return cls._handle_occupant_selection(data, token_dict, request)
|
||||
# return ResponseHandler.error(
|
||||
# "Invalid token type", status_code=status.HTTP_400_BAD_REQUEST
|
||||
# )
|
||||
# except Exception as e:
|
||||
# return ResponseHandler.error(
|
||||
# str(e), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
# )
|
||||
return
|
||||
|
||||
|
||||
class AuthenticationCheckTokenEventMethods(MethodToEvent):
|
||||
event_type = "LOGIN"
|
||||
|
|
@ -300,7 +150,7 @@ class AuthenticationCheckTokenEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_check_token_is_valid(
|
||||
async def authentication_check_token_is_valid(
|
||||
cls, request: "Request", data: DictRequestModel
|
||||
):
|
||||
# try:
|
||||
|
|
@ -324,7 +174,7 @@ class AuthenticationRefreshEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_refresh_user_info(
|
||||
async def authentication_refresh_user_info(
|
||||
cls,
|
||||
request: "Request",
|
||||
token_dict: TokenDictType,
|
||||
|
|
@ -371,7 +221,7 @@ class AuthenticationChangePasswordEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_change_password(
|
||||
async def authentication_change_password(
|
||||
cls,
|
||||
request: "Request",
|
||||
data: ChangePasswordRequestModel,
|
||||
|
|
@ -429,7 +279,7 @@ class AuthenticationCreatePasswordEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_create_password(
|
||||
async def authentication_create_password(
|
||||
cls, request: "Request", data: CreatePasswordRequestModel
|
||||
):
|
||||
|
||||
|
|
@ -475,7 +325,7 @@ class AuthenticationDisconnectUserEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_disconnect_user(
|
||||
async def authentication_disconnect_user(
|
||||
cls, request: "Request", data: LogoutRequestModel, token_dict: TokenDictType
|
||||
):
|
||||
# found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data
|
||||
|
|
@ -514,11 +364,11 @@ class AuthenticationLogoutEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_logout_user(
|
||||
async def authentication_logout_user(
|
||||
cls,
|
||||
request: "Request",
|
||||
data: LogoutRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
token_dict: TokenDictType,
|
||||
):
|
||||
# token_user = None
|
||||
# if already_tokens := RedisActions.get_object_via_access_key(request=request):
|
||||
|
|
@ -553,7 +403,7 @@ class AuthenticationRefreshTokenEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_refresher_token(
|
||||
async def authentication_refresher_token(
|
||||
cls, request: "Request", data: RememberRequestModel, token_dict: TokenDictType
|
||||
):
|
||||
# token_refresher = UsersTokens.filter_by_one(
|
||||
|
|
@ -602,7 +452,7 @@ class AuthenticationForgotPasswordEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_forgot_password(
|
||||
async def authentication_forgot_password(
|
||||
cls,
|
||||
request: "Request",
|
||||
data: ForgotRequestModel,
|
||||
|
|
@ -644,7 +494,7 @@ class AuthenticationResetPasswordEventMethods(MethodToEvent):
|
|||
}
|
||||
|
||||
@classmethod
|
||||
def authentication_reset_password(
|
||||
async def authentication_reset_password(
|
||||
cls, request: "Request", data: ForgotRequestModel
|
||||
):
|
||||
# from sqlalchemy import or_
|
||||
|
|
@ -695,11 +545,11 @@ class AuthenticationDownloadAvatarEventMethods(MethodToEvent):
|
|||
# }
|
||||
|
||||
@classmethod
|
||||
def authentication_download_avatar(
|
||||
async def authentication_download_avatar(
|
||||
cls,
|
||||
token_dict: TokenDictType,
|
||||
request: "Request",
|
||||
data: DictRequestModel,
|
||||
token_dict: TokenDictType,
|
||||
):
|
||||
# if found_user := Users.filter_one(Users.id == token_dict.user_id).data:
|
||||
# expired_starts = str(
|
||||
|
|
@ -725,253 +575,3 @@ class AuthenticationDownloadAvatarEventMethods(MethodToEvent):
|
|||
# return ResponseHandler.not_found("Invalid data")
|
||||
|
||||
return
|
||||
|
||||
|
||||
async def authentication_select_company_or_occupant_type(
|
||||
request: "Request",
|
||||
data: Union[EmployeeSelectionRequestModel, OccupantSelectionRequestModel],
|
||||
) -> Dict[str, Any]:
|
||||
return await AuthenticationSelectEventMethods.authentication_select_company_or_occupant_type(
|
||||
request=request, data=data
|
||||
)
|
||||
|
||||
|
||||
async def authentication_login_with_domain_and_creds(
|
||||
request: "Request", data: LoginRequestModel
|
||||
) -> SuccessResponse:
|
||||
"""
|
||||
Authenticate user with domain and credentials.
|
||||
|
||||
Args:
|
||||
request: FastAPI request object
|
||||
data: Request body containing login credentials
|
||||
{
|
||||
"domain": str,
|
||||
"username": str,
|
||||
"password": str
|
||||
}
|
||||
|
||||
Returns:
|
||||
SuccessResponse containing authentication token and user info
|
||||
"""
|
||||
return await AuthenticationLoginEventMethods.authentication_login_with_domain_and_creds(
|
||||
request=request, data=data
|
||||
)
|
||||
|
||||
|
||||
async def authentication_check_token_is_valid(
|
||||
request: "Request", data: DictRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return (
|
||||
await AuthenticationCheckTokenEventMethods.authentication_check_token_is_valid(
|
||||
request=request, data=data
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def authentication_refresh_user_info(
|
||||
request: "Request", data: DictRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
def authentication_change_password(
|
||||
request: "Request", data: ChangePasswordRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
def authentication_create_password(
|
||||
request: "Request", data: CreatePasswordRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
def authentication_forgot_password(
|
||||
request: "Request", data: ForgotRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
def authentication_reset_password(
|
||||
request: "Request", data: ForgotRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
def authentication_disconnect_user(
|
||||
request: "Request", data: LogoutRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
def authentication_logout_user(
|
||||
request: "Request", data: LogoutRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
def authentication_refresher_token(
|
||||
request: "Request", data: RememberRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
def authentication_download_avatar(
|
||||
request: "Request", data: DictRequestModel
|
||||
) -> Dict[str, Any]:
|
||||
return
|
||||
|
||||
|
||||
AUTH_CONFIG = RouteFactoryConfig(
|
||||
name="authentication",
|
||||
prefix="/authentication",
|
||||
tags=["Authentication"],
|
||||
include_in_schema=True,
|
||||
endpoints=[
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/select",
|
||||
url_of_endpoint="/authentication/select",
|
||||
endpoint="/select",
|
||||
method="POST",
|
||||
summary="Select company or occupant type",
|
||||
description="Select company or occupant type",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_select_company_or_occupant_type,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/login",
|
||||
url_of_endpoint="/authentication/login",
|
||||
endpoint="/login",
|
||||
method="POST",
|
||||
summary="Login user with domain and password",
|
||||
description="Login user with domain and password",
|
||||
is_auth_required=False, # Public endpoint
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_login_with_domain_and_creds,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/valid",
|
||||
url_of_endpoint="/authentication/valid",
|
||||
endpoint="/valid",
|
||||
method="GET",
|
||||
summary="Check access token is valid",
|
||||
description="Check access token is valid",
|
||||
is_auth_required=True, # Needs token validation
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_check_token_is_valid,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/refresh",
|
||||
url_of_endpoint="/authentication/refresh",
|
||||
endpoint="/refresh",
|
||||
method="GET",
|
||||
summary="Refresh credentials with access token",
|
||||
description="Refresh credentials with access token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_refresh_user_info,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/change_password",
|
||||
url_of_endpoint="/authentication/change_password",
|
||||
endpoint="/change_password",
|
||||
method="POST",
|
||||
summary="Change password with access token",
|
||||
description="Change password with access token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_change_password,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/create_password",
|
||||
url_of_endpoint="/authentication/create_password",
|
||||
endpoint="/create_password",
|
||||
method="POST",
|
||||
summary="Create password with password token",
|
||||
description="Create password with password token",
|
||||
is_auth_required=False, # Public endpoint
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_create_password,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/reset_password",
|
||||
url_of_endpoint="/authentication/reset_password",
|
||||
endpoint="/reset_password",
|
||||
method="POST",
|
||||
summary="Create password with password token",
|
||||
description="Create password with password token",
|
||||
is_auth_required=False, # Public endpoint
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_reset_password,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/disconnect",
|
||||
url_of_endpoint="/authentication/disconnect",
|
||||
endpoint="/disconnect",
|
||||
method="POST",
|
||||
summary="Disconnect user with access token",
|
||||
description="Disconnect user with access token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_disconnect_user,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/logout",
|
||||
url_of_endpoint="/authentication/logout",
|
||||
endpoint="/logout",
|
||||
method="POST",
|
||||
summary="Logout user with access token",
|
||||
description="Logout user with access token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_logout_user,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/refresher",
|
||||
url_of_endpoint="/authentication/refresher",
|
||||
endpoint="/refresher",
|
||||
method="POST",
|
||||
summary="Refresh token with refresh token",
|
||||
description="Refresh token with refresh token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_refresher_token,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/forgot",
|
||||
url_of_endpoint="/authentication/forgot",
|
||||
endpoint="/forgot",
|
||||
method="POST",
|
||||
summary="Forgot password with email or phone number",
|
||||
description="Forgot password with email or phone number",
|
||||
is_auth_required=False, # Public endpoint
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_forgot_password,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix="/authentication",
|
||||
url_endpoint="/avatar",
|
||||
url_of_endpoint="/authentication/avatar",
|
||||
endpoint="/avatar",
|
||||
method="POST",
|
||||
summary="Get link of avatar with credentials",
|
||||
description="Get link of avatar with credentials",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_download_avatar,
|
||||
),
|
||||
],
|
||||
).as_dict()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,371 @@
|
|||
"""
|
||||
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 .auth import (
|
||||
AuthenticationChangePasswordEventMethods,
|
||||
AuthenticationCheckTokenEventMethods,
|
||||
AuthenticationCreatePasswordEventMethods,
|
||||
AuthenticationDisconnectUserEventMethods,
|
||||
AuthenticationDownloadAvatarEventMethods,
|
||||
AuthenticationForgotPasswordEventMethods,
|
||||
AuthenticationLoginEventMethods,
|
||||
AuthenticationLogoutEventMethods,
|
||||
AuthenticationRefreshEventMethods,
|
||||
AuthenticationRefreshTokenEventMethods,
|
||||
AuthenticationResetPasswordEventMethods,
|
||||
AuthenticationSelectEventMethods,
|
||||
)
|
||||
from .models import (
|
||||
ChangePasswordRequestModel,
|
||||
CreatePasswordRequestModel,
|
||||
ForgotRequestModel,
|
||||
LoginData,
|
||||
LoginRequestModel,
|
||||
LogoutRequestModel,
|
||||
SelectionDataEmployee,
|
||||
SelectionDataOccupant,
|
||||
RememberRequestModel,
|
||||
)
|
||||
from ApiEvents.base_request_model import DictRequestModel
|
||||
from ApiEvents.abstract_class import RouteFactoryConfig, EndpointFactoryConfig, endpoint_wrapper
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastapi import Request
|
||||
from ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
|
||||
|
||||
|
||||
# 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
|
||||
) -> 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
|
||||
"""
|
||||
return {
|
||||
"headers": dict(request.headers),
|
||||
"data": data,
|
||||
"token": token_dict
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/login")
|
||||
async def authentication_login_with_domain_and_creds(
|
||||
request: "Request",
|
||||
data: Login,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Authenticate user with domain and credentials.
|
||||
"""
|
||||
return AuthenticationLoginEventMethods.authentication_login_with_domain_and_creds(
|
||||
request=request, data=data
|
||||
)
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/check")
|
||||
async def authentication_check_token_is_valid(
|
||||
request: "Request",
|
||||
data: DictRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Check if a token is valid.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/refresh")
|
||||
async def authentication_refresh_user_info(
|
||||
request: "Request",
|
||||
data: DictRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Refresh user information.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/change-password")
|
||||
async def authentication_change_password(
|
||||
request: "Request",
|
||||
data: ChangePasswordRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Change user password.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/create-password")
|
||||
async def authentication_create_password(
|
||||
request: "Request",
|
||||
data: CreatePasswordRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Create new password.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
@endpoint_wrapper("/authentication/forgot-password")
|
||||
async def authentication_forgot_password(
|
||||
request: "Request",
|
||||
data: ForgotRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Handle forgot password request.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
@endpoint_wrapper("/authentication/reset-password")
|
||||
async def authentication_reset_password(
|
||||
request: "Request",
|
||||
data: ForgotRequestModel,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Reset password.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
@endpoint_wrapper("/authentication/disconnect")
|
||||
async def authentication_disconnect_user(
|
||||
request: "Request",
|
||||
data: LogoutRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Disconnect user.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/logout")
|
||||
async def authentication_logout_user(
|
||||
request: "Request",
|
||||
data: LogoutRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Logout user.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/remember")
|
||||
async def authentication_refresher_token(
|
||||
request: "Request",
|
||||
data: RememberRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Refresh remember token.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/authentication/avatar")
|
||||
async def authentication_download_avatar(
|
||||
request: "Request",
|
||||
data: DictRequestModel,
|
||||
token_dict: TokenDictType = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Download user avatar.
|
||||
"""
|
||||
return {
|
||||
"status": "OK",
|
||||
}
|
||||
|
||||
|
||||
prefix = "/authentication"
|
||||
|
||||
|
||||
AUTH_CONFIG = RouteFactoryConfig(
|
||||
name="authentication",
|
||||
prefix=prefix,
|
||||
tags=["Authentication"],
|
||||
include_in_schema=True,
|
||||
endpoints=[
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/select",
|
||||
url_of_endpoint="/authentication/select",
|
||||
endpoint="/select",
|
||||
method="POST",
|
||||
summary="Select company or occupant type",
|
||||
description="Select company or occupant type",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_select_company_or_occupant_type,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/login",
|
||||
url_of_endpoint="/authentication/login",
|
||||
endpoint="/login",
|
||||
method="POST",
|
||||
summary="Login user with domain and password",
|
||||
description="Login user with domain and password",
|
||||
is_auth_required=False, # Public endpoint
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_login_with_domain_and_creds,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/check",
|
||||
url_of_endpoint="/authentication/check",
|
||||
endpoint="/check",
|
||||
method="GET",
|
||||
summary="Check access token is valid",
|
||||
description="Check access token is valid",
|
||||
is_auth_required=True, # Needs token validation
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_check_token_is_valid,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/refresh",
|
||||
url_of_endpoint="/authentication/refresh",
|
||||
endpoint="/refresh",
|
||||
method="GET",
|
||||
summary="Refresh credentials with access token",
|
||||
description="Refresh credentials with access token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_refresh_user_info,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/change-password",
|
||||
url_of_endpoint="/authentication/change-password",
|
||||
endpoint="/change-password",
|
||||
method="POST",
|
||||
summary="Change password with access token",
|
||||
description="Change password with access token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_change_password,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/create-password",
|
||||
url_of_endpoint="/authentication/create-password",
|
||||
endpoint="/create-password",
|
||||
method="POST",
|
||||
summary="Create password with password token",
|
||||
description="Create password with password token",
|
||||
is_auth_required=False, # Public endpoint
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_create_password,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/reset-password",
|
||||
url_of_endpoint="/authentication/reset-password",
|
||||
endpoint="/reset-password",
|
||||
method="POST",
|
||||
summary="Reset password with token",
|
||||
description="Reset password with token",
|
||||
is_auth_required=False, # Public endpoint
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_reset_password,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/disconnect",
|
||||
url_of_endpoint="/authentication/disconnect",
|
||||
endpoint="/disconnect",
|
||||
method="POST",
|
||||
summary="Disconnect user with access token",
|
||||
description="Disconnect user with access token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_disconnect_user,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/logout",
|
||||
url_of_endpoint="/authentication/logout",
|
||||
endpoint="/logout",
|
||||
method="POST",
|
||||
summary="Logout user with access token",
|
||||
description="Logout user with access token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_logout_user,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/remember",
|
||||
url_of_endpoint="/authentication/remember",
|
||||
endpoint="/remember",
|
||||
method="POST",
|
||||
summary="Refresh token with refresh token",
|
||||
description="Refresh token with refresh token",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_refresher_token,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/forgot-password",
|
||||
url_of_endpoint="/authentication/forgot-password",
|
||||
endpoint="/forgot-password",
|
||||
method="POST",
|
||||
summary="Request password reset via email",
|
||||
description="Request password reset via email",
|
||||
is_auth_required=False, # Public endpoint
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_forgot_password,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/avatar",
|
||||
url_of_endpoint="/authentication/avatar",
|
||||
endpoint="/avatar",
|
||||
method="POST",
|
||||
summary="Get user avatar with credentials",
|
||||
description="Get user avatar with credentials",
|
||||
is_auth_required=True, # Needs token_dict
|
||||
is_event_required=False,
|
||||
endpoint_function=authentication_download_avatar,
|
||||
),
|
||||
],
|
||||
).as_dict()
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
"""
|
||||
Authentication request models.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Dict, Any, Literal, Optional, TypedDict
|
||||
from pydantic import BaseModel, Field, model_validator, RootModel, ConfigDict
|
||||
from ApiEvents.base_request_model import BaseRequestModel, DictRequestModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastapi import Request
|
||||
|
||||
|
||||
class TokenObjectBase(BaseModel):
|
||||
"""Base model for token objects."""
|
||||
user_type: str = Field(..., description="Type of user")
|
||||
user_id: str = Field(..., description="User ID")
|
||||
token: str = Field(..., description="Authentication token")
|
||||
permissions: Dict[str, Any] = Field(default_factory=dict, description="User permissions")
|
||||
|
||||
|
||||
|
||||
class LoginData(TypedDict):
|
||||
"""Type for login data."""
|
||||
domain: str
|
||||
access_key: str
|
||||
password: str
|
||||
remember_me: bool
|
||||
|
||||
|
||||
class LoginRequestModel(BaseRequestModel[LoginData]):
|
||||
"""Request model for login endpoint."""
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"domain": "example.com",
|
||||
"access_key": "user@example",
|
||||
"password": "password",
|
||||
"remember_me": False
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class LogoutData(TypedDict):
|
||||
"""Type for logout data."""
|
||||
token: str
|
||||
|
||||
|
||||
class LogoutRequestModel(BaseRequestModel[LogoutData]):
|
||||
"""Request model for logout endpoint."""
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"token": "your-token-here"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class RememberData(TypedDict):
|
||||
"""Type for remember token data."""
|
||||
remember_token: str
|
||||
|
||||
|
||||
class RememberRequestModel(BaseRequestModel[RememberData]):
|
||||
"""Request model for remember token endpoint."""
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"remember_token": "your-remember-token-here"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ForgotData(TypedDict):
|
||||
"""Type for forgot password data."""
|
||||
email: str
|
||||
domain: str
|
||||
|
||||
|
||||
class ForgotRequestModel(BaseRequestModel[ForgotData]):
|
||||
"""Request model for forgot password endpoint."""
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"email": "user@example.com",
|
||||
"domain": "example.com"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ChangePasswordData(TypedDict):
|
||||
"""Type for change password data."""
|
||||
old_password: str
|
||||
new_password: str
|
||||
|
||||
|
||||
class ChangePasswordRequestModel(BaseRequestModel[ChangePasswordData]):
|
||||
"""Request model for change password endpoint."""
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"old_password": "old-pass",
|
||||
"new_password": "new-pass"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class CreatePasswordData(TypedDict):
|
||||
"""Type for create password data."""
|
||||
token: str
|
||||
password: str
|
||||
|
||||
|
||||
class CreatePasswordRequestModel(BaseRequestModel[CreatePasswordData]):
|
||||
"""Request model for create password endpoint."""
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"token": "password-creation-token",
|
||||
"password": "new-password"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class SelectionDataOccupant(TypedDict):
|
||||
"""Type for selection data."""
|
||||
build_living_space_uu_id: Optional[str]
|
||||
|
||||
|
||||
class SelectionDataEmployee(TypedDict):
|
||||
"""Type for selection data."""
|
||||
company_uu_id: Optional[str]
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ to be used by the dynamic route creation system.
|
|||
"""
|
||||
|
||||
from typing import Dict, List, Any
|
||||
from .auth.auth import AUTH_CONFIG
|
||||
from .auth.endpoints import AUTH_CONFIG
|
||||
|
||||
|
||||
# Registry of all route configurations
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
"""
|
||||
Account records package initialization.
|
||||
"""
|
||||
|
||||
from .endpoints import ACCOUNT_RECORDS_CONFIG
|
||||
|
||||
__all__ = [
|
||||
"ACCOUNT_RECORDS_CONFIG",
|
||||
]
|
||||
|
|
@ -1,24 +1,22 @@
|
|||
import typing
|
||||
"""
|
||||
Account records service implementation.
|
||||
"""
|
||||
|
||||
from ApiEvents.abstract_class import (
|
||||
MethodToEvent,
|
||||
RouteFactoryConfig,
|
||||
EndpointFactoryConfig,
|
||||
endpoint_wrapper,
|
||||
)
|
||||
from ApiEvents.base_request_model import BaseRequestModel, DictRequestModel
|
||||
|
||||
from typing import TYPE_CHECKING, Dict, Any
|
||||
from fastapi import Request, Path, Body, Depends, APIRouter
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastapi import Request
|
||||
from typing import Dict, Any, Union
|
||||
from pydantic import Field
|
||||
|
||||
from ApiEvents.abstract_class import MethodToEvent, endpoint_wrapper
|
||||
from ApiEvents.base_request_model import DictRequestModel
|
||||
from ApiValidations.Custom.token_objects import (
|
||||
OccupantTokenObject,
|
||||
EmployeeTokenObject,
|
||||
)
|
||||
from ApiLibrary import system_arrow
|
||||
from ApiValidations.Request.account_records import (
|
||||
InsertAccountRecord,
|
||||
UpdateAccountRecord,
|
||||
)
|
||||
from ApiValidations.Request.base_validations import ListOptions
|
||||
from Schemas import (
|
||||
BuildLivingSpace,
|
||||
AccountRecords,
|
||||
|
|
@ -26,49 +24,16 @@ from Schemas import (
|
|||
BuildDecisionBookPayments,
|
||||
ApiEnumDropdown,
|
||||
)
|
||||
from ApiLibrary import system_arrow
|
||||
from ApiValidations.Request import (
|
||||
InsertAccountRecord,
|
||||
UpdateAccountRecord,
|
||||
ListOptions,
|
||||
)
|
||||
from Services.PostgresDb.Models.alchemy_response import (
|
||||
AlchemyJsonResponse,
|
||||
DictJsonResponse,
|
||||
)
|
||||
from ApiValidations.Response import AccountRecordResponse
|
||||
|
||||
|
||||
class AddressUpdateRequest(BaseModel):
|
||||
"""Request model for address update."""
|
||||
|
||||
data: Dict[str, Any] = Field(..., description="Updated address data")
|
||||
|
||||
|
||||
class AddressUpdateResponse(BaseModel):
|
||||
"""Response model for address update."""
|
||||
|
||||
address_uu_id: str = Field(..., description="UUID of the updated address")
|
||||
data: Dict[str, Any] = Field(..., description="Updated address data")
|
||||
function_code: str = Field(..., description="Function code for the endpoint")
|
||||
|
||||
|
||||
class InsertAccountRecordRequestModel(BaseRequestModel[InsertAccountRecord]):
|
||||
"""Request model for inserting account records."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UpdateAccountRecordRequestModel(BaseRequestModel[UpdateAccountRecord]):
|
||||
"""Request model for updating account records."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ListOptionsRequestModel(BaseRequestModel[ListOptions]):
|
||||
"""Request model for list options."""
|
||||
|
||||
pass
|
||||
from .models import (
|
||||
InsertAccountRecordRequestModel,
|
||||
UpdateAccountRecordRequestModel,
|
||||
ListOptionsRequestModel,
|
||||
)
|
||||
|
||||
|
||||
class AccountRecordsListEventMethods(MethodToEvent):
|
||||
|
|
@ -90,7 +55,7 @@ class AccountRecordsListEventMethods(MethodToEvent):
|
|||
def account_records_list(
|
||||
cls,
|
||||
list_options: ListOptionsRequestModel,
|
||||
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
|
||||
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||
):
|
||||
db_session = AccountRecords.new_session()
|
||||
if isinstance(token_dict, OccupantTokenObject):
|
||||
|
|
@ -119,7 +84,7 @@ class AccountRecordsListEventMethods(MethodToEvent):
|
|||
def account_records_list_flt_res(
|
||||
cls,
|
||||
list_options: ListOptionsRequestModel,
|
||||
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
|
||||
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||
):
|
||||
db_session = AccountRecords.new_session()
|
||||
if not isinstance(token_dict, OccupantTokenObject):
|
||||
|
|
@ -269,7 +234,7 @@ class AccountRecordsCreateEventMethods(MethodToEvent):
|
|||
def account_records_create(
|
||||
cls,
|
||||
data: InsertAccountRecordRequestModel,
|
||||
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
|
||||
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||
):
|
||||
data_dict = data.excluded_dump()
|
||||
if isinstance(token_dict, OccupantTokenObject):
|
||||
|
|
@ -358,7 +323,7 @@ class AccountRecordsUpdateEventMethods(MethodToEvent):
|
|||
cls,
|
||||
build_uu_id: str,
|
||||
data: UpdateAccountRecordRequestModel,
|
||||
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
|
||||
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
|
||||
):
|
||||
if isinstance(token_dict, OccupantTokenObject):
|
||||
pass
|
||||
|
|
@ -373,123 +338,3 @@ class AccountRecordsUpdateEventMethods(MethodToEvent):
|
|||
cls_object=AccountRecords,
|
||||
response_model=UpdateAccountRecord,
|
||||
)
|
||||
|
||||
|
||||
@endpoint_wrapper("/account/records/address/list")
|
||||
async def address_list(request: "Request", data: ListOptionsRequestModel):
|
||||
"""Handle address list endpoint."""
|
||||
return {
|
||||
"data": data,
|
||||
"request": str(request.headers),
|
||||
"request_url": str(request.url),
|
||||
"request_base_url": str(request.base_url),
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/account/records/address/create")
|
||||
async def address_create(request: "Request", data: DictRequestModel):
|
||||
"""Handle address creation endpoint."""
|
||||
return {
|
||||
"data": data,
|
||||
"request": str(request.headers),
|
||||
"request_url": str(request.url),
|
||||
"request_base_url": str(request.base_url),
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/account/records/address/search")
|
||||
async def address_search(request: "Request", data: DictRequestModel):
|
||||
"""Handle address search endpoint."""
|
||||
return {"data": data}
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@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"),
|
||||
):
|
||||
"""
|
||||
Handle address update endpoint.
|
||||
|
||||
Args:
|
||||
request: FastAPI request object
|
||||
address_uu_id: UUID of the address to update
|
||||
request_data: Request body containing updated address data
|
||||
|
||||
Returns:
|
||||
DictJsonResponse: Response containing updated address info
|
||||
"""
|
||||
return DictJsonResponse(
|
||||
data={
|
||||
"address_uu_id": address_uu_id,
|
||||
"data": request_data.root,
|
||||
"request": str(request.headers),
|
||||
"request_url": str(request.url),
|
||||
"request_base_url": str(request.base_url),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
prefix = "/account/records"
|
||||
|
||||
# Account Records Router Configuration
|
||||
ACCOUNT_RECORDS_CONFIG = RouteFactoryConfig(
|
||||
name="account_records",
|
||||
prefix=prefix,
|
||||
tags=["Account Records"],
|
||||
include_in_schema=True,
|
||||
endpoints=[
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/address/list",
|
||||
url_of_endpoint="/account/records/address/list",
|
||||
endpoint="/address/list",
|
||||
method="POST",
|
||||
summary="List Active/Delete/Confirm Address",
|
||||
description="List Active/Delete/Confirm Address",
|
||||
is_auth_required=True,
|
||||
is_event_required=True,
|
||||
endpoint_function=address_list,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/address/create",
|
||||
url_of_endpoint="/account/records/address/create",
|
||||
endpoint="/address/create",
|
||||
method="POST",
|
||||
summary="Create Address with given auth levels",
|
||||
description="Create Address with given auth levels",
|
||||
is_auth_required=False,
|
||||
is_event_required=False,
|
||||
endpoint_function=address_create,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/address/search",
|
||||
url_of_endpoint="/account/records/address/search",
|
||||
endpoint="/address/search",
|
||||
method="POST",
|
||||
summary="Search Address with given auth levels",
|
||||
description="Search Address with given auth levels",
|
||||
is_auth_required=True,
|
||||
is_event_required=True,
|
||||
endpoint_function=address_search,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/address/{address_uu_id}",
|
||||
url_of_endpoint="/account/records/address/{address_uu_id}",
|
||||
endpoint="/address/{address_uu_id}",
|
||||
method="PUT",
|
||||
summary="Update Address with given auth levels",
|
||||
description="Update Address with given auth levels",
|
||||
is_auth_required=True,
|
||||
is_event_required=True,
|
||||
endpoint_function=address_update,
|
||||
),
|
||||
],
|
||||
).as_dict()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
"""
|
||||
Account records endpoint configurations.
|
||||
|
||||
"""
|
||||
|
||||
from ApiEvents.abstract_class import RouteFactoryConfig, EndpointFactoryConfig, endpoint_wrapper
|
||||
from ApiEvents.base_request_model import DictRequestModel
|
||||
|
||||
|
||||
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):
|
||||
"""Handle address list endpoint."""
|
||||
return {
|
||||
"data": data,
|
||||
"request": str(request.headers),
|
||||
"request_url": str(request.url),
|
||||
"request_base_url": str(request.base_url),
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/account/records/address/create")
|
||||
async def address_create(request: "Request", data: DictRequestModel):
|
||||
"""Handle address creation endpoint."""
|
||||
return {
|
||||
"data": data,
|
||||
"request": str(request.headers),
|
||||
"request_url": str(request.url),
|
||||
"request_base_url": str(request.base_url),
|
||||
}
|
||||
|
||||
|
||||
@endpoint_wrapper("/account/records/address/search")
|
||||
async def address_search(request: "Request", data: DictRequestModel):
|
||||
"""Handle address search endpoint."""
|
||||
return {"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"),
|
||||
):
|
||||
"""
|
||||
Handle address update endpoint.
|
||||
|
||||
Args:
|
||||
request: FastAPI request object
|
||||
address_uu_id: UUID of the address to update
|
||||
request_data: Request body containing updated address data
|
||||
|
||||
Returns:
|
||||
DictJsonResponse: Response containing updated address info
|
||||
"""
|
||||
return DictJsonResponse(
|
||||
data={
|
||||
"address_uu_id": address_uu_id,
|
||||
"data": request_data.root,
|
||||
"request": str(request.headers),
|
||||
"request_url": str(request.url),
|
||||
"request_base_url": str(request.base_url),
|
||||
}
|
||||
)
|
||||
|
||||
prefix = "/account/records"
|
||||
|
||||
# Account Records Router Configuration
|
||||
ACCOUNT_RECORDS_CONFIG = RouteFactoryConfig(
|
||||
name="account_records",
|
||||
prefix=prefix,
|
||||
tags=["Account Records"],
|
||||
include_in_schema=True,
|
||||
endpoints=[
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/address/list",
|
||||
url_of_endpoint="/account/records/address/list",
|
||||
endpoint="/address/list",
|
||||
method="POST",
|
||||
summary="List Active/Delete/Confirm Address",
|
||||
description="List Active/Delete/Confirm Address",
|
||||
is_auth_required=True,
|
||||
is_event_required=True,
|
||||
endpoint_function=address_list,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/address/create",
|
||||
url_of_endpoint="/account/records/address/create",
|
||||
endpoint="/address/create",
|
||||
method="POST",
|
||||
summary="Create Address with given auth levels",
|
||||
description="Create Address with given auth levels",
|
||||
is_auth_required=False,
|
||||
is_event_required=False,
|
||||
endpoint_function=address_create,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/address/search",
|
||||
url_of_endpoint="/account/records/address/search",
|
||||
endpoint="/address/search",
|
||||
method="POST",
|
||||
summary="Search Address with given auth levels",
|
||||
description="Search Address with given auth levels",
|
||||
is_auth_required=True,
|
||||
is_event_required=True,
|
||||
endpoint_function=address_search,
|
||||
),
|
||||
EndpointFactoryConfig(
|
||||
url_prefix=prefix,
|
||||
url_endpoint="/address/{address_uu_id}",
|
||||
url_of_endpoint="/account/records/address/{address_uu_id}",
|
||||
endpoint="/address/{address_uu_id}",
|
||||
method="PUT",
|
||||
summary="Update Address with given auth levels",
|
||||
description="Update Address with given auth levels",
|
||||
is_auth_required=True,
|
||||
is_event_required=True,
|
||||
endpoint_function=address_update,
|
||||
),
|
||||
],
|
||||
).as_dict()
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
"""
|
||||
Account records request and response models.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Dict, Any
|
||||
from pydantic import BaseModel, Field, RootModel
|
||||
from ApiEvents.base_request_model import BaseRequestModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ApiValidations.Request import (
|
||||
InsertAccountRecord,
|
||||
UpdateAccountRecord,
|
||||
ListOptions,
|
||||
)
|
||||
|
||||
|
||||
class AddressUpdateRequest(RootModel[Dict[str, Any]]):
|
||||
"""Request model for address update."""
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"street": "123 Main St",
|
||||
"city": "Example City",
|
||||
"country": "Example Country"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AddressUpdateResponse(BaseModel):
|
||||
"""Response model for address update."""
|
||||
address_uu_id: str = Field(..., description="UUID of the updated address")
|
||||
data: Dict[str, Any] = Field(..., description="Updated address data")
|
||||
function_code: str = Field(..., description="Function code for the endpoint")
|
||||
|
||||
|
||||
class InsertAccountRecordRequestModel(BaseRequestModel["InsertAccountRecord"]):
|
||||
"""Request model for inserting account records."""
|
||||
pass
|
||||
|
||||
|
||||
class UpdateAccountRecordRequestModel(BaseRequestModel["UpdateAccountRecord"]):
|
||||
"""Request model for updating account records."""
|
||||
pass
|
||||
|
||||
|
||||
class ListOptionsRequestModel(BaseRequestModel["ListOptions"]):
|
||||
"""Request model for list options."""
|
||||
pass
|
||||
|
|
@ -6,7 +6,7 @@ to be used by the dynamic route creation system.
|
|||
"""
|
||||
|
||||
from typing import Dict, List, Any
|
||||
from .account.account_records import ACCOUNT_RECORDS_CONFIG
|
||||
from .account.endpoints import ACCOUNT_RECORDS_CONFIG
|
||||
|
||||
|
||||
# Registry of all route configurations
|
||||
|
|
|
|||
|
|
@ -5,54 +5,22 @@ This module provides base request models that can be used across different endpo
|
|||
to ensure consistent request handling and validation.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Generic, TypeVar, Optional, get_args, get_origin
|
||||
from pydantic import RootModel, BaseModel, Field, ConfigDict
|
||||
from typing import Dict, Any, Generic, TypeVar, Optional, Union, get_args
|
||||
from pydantic import BaseModel, Field, ConfigDict, RootModel
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class BaseRequestModel(RootModel[T]):
|
||||
"""Base model for all API requests.
|
||||
|
||||
This model can be extended to create specific request models for different endpoints.
|
||||
"""
|
||||
|
||||
class BaseRequestModel(RootModel[T], Generic[T]):
|
||||
"""Base model for all API requests."""
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={"example": {}} # Will be populated by subclasses
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def model_json_schema(cls, *args, **kwargs):
|
||||
schema = super().model_json_schema(*args, **kwargs)
|
||||
if hasattr(cls, "__orig_bases__"):
|
||||
generic_type = get_args(cls.__orig_bases__[0])[0]
|
||||
if generic_type and hasattr(generic_type, "model_json_schema"):
|
||||
type_schema = generic_type.model_json_schema()
|
||||
if "properties" in type_schema:
|
||||
schema["properties"] = type_schema["properties"]
|
||||
if "required" in type_schema:
|
||||
schema["required"] = type_schema["required"]
|
||||
if "title" in type_schema:
|
||||
schema["title"] = type_schema["title"]
|
||||
if "example" in type_schema:
|
||||
schema["example"] = type_schema["example"]
|
||||
elif "properties" in type_schema:
|
||||
schema["example"] = {
|
||||
key: prop.get("example", "string")
|
||||
for key, prop in type_schema["properties"].items()
|
||||
}
|
||||
schema["type"] = "object"
|
||||
return schema
|
||||
|
||||
@property
|
||||
def data(self) -> T:
|
||||
return self.root
|
||||
|
||||
|
||||
class DictRequestModel(RootModel[Dict[str, Any]]):
|
||||
"""Request model for endpoints that accept dictionary data."""
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
|
|
@ -63,40 +31,9 @@ class DictRequestModel(RootModel[Dict[str, Any]]):
|
|||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def model_json_schema(cls, *args, **kwargs):
|
||||
schema = super().model_json_schema(*args, **kwargs)
|
||||
schema.update(
|
||||
{
|
||||
"title": "Dictionary Request Model",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key1": {"type": "string", "example": "value1"},
|
||||
"key2": {"type": "string", "example": "value2"},
|
||||
"nested": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"inner_key": {"type": "string", "example": "inner_value"}
|
||||
},
|
||||
},
|
||||
},
|
||||
"example": {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"nested": {"inner_key": "inner_value"},
|
||||
},
|
||||
}
|
||||
)
|
||||
return schema
|
||||
|
||||
@property
|
||||
def data(self) -> Dict[str, Any]:
|
||||
return self.root
|
||||
|
||||
|
||||
class SuccessResponse(BaseModel):
|
||||
"""Standard success response model."""
|
||||
|
||||
token: str = Field(..., example="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
|
||||
user_info: Dict[str, Any] = Field(
|
||||
...,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
def get_line_number_for_error():
|
||||
from inspect import currentframe, getframeinfo
|
||||
"""Utility functions for getting line numbers and file locations."""
|
||||
|
||||
frameinfo = getframeinfo(currentframe())
|
||||
from inspect import currentframe, getframeinfo, stack
|
||||
|
||||
|
||||
def get_line_number_for_error() -> str:
|
||||
"""Get the file name and line number of where an error occurred.
|
||||
|
||||
Returns:
|
||||
str: A string in the format 'filename | line_number' showing where the error occurred
|
||||
"""
|
||||
caller = stack()[1] # Get the caller's frame
|
||||
frameinfo = getframeinfo(caller[0])
|
||||
return f"{frameinfo.filename} | {frameinfo.lineno}"
|
||||
|
|
|
|||
|
|
@ -30,12 +30,14 @@ class UserLoginModule:
|
|||
error_code="HTTP_400_BAD_REQUEST",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="User not found",
|
||||
)
|
||||
return found_user
|
||||
|
||||
async def login_user_via_credentials(self, access_data: "Login") -> Dict[str, Any]:
|
||||
"""Login user via credentials."""
|
||||
# Get the actual data from the BaseRequestModel if needed
|
||||
print("access_data", access_data)
|
||||
if hasattr(access_data, "data"):
|
||||
access_data = access_data.data
|
||||
|
||||
|
|
@ -47,6 +49,7 @@ class UserLoginModule:
|
|||
error_code="HTTP_400_BAD_REQUEST",
|
||||
lang=found_user.lang,
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="Invalid password create a password to user first",
|
||||
)
|
||||
|
||||
if PasswordModule.check_password(
|
||||
|
|
@ -65,4 +68,5 @@ class UserLoginModule:
|
|||
error_code="HTTP_400_BAD_REQUEST",
|
||||
lang=found_user.lang,
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="login_user_via_credentials raised error",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ class TokenService:
|
|||
).data
|
||||
if not living_spaces:
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
error_code="",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="User does not have any living space",
|
||||
)
|
||||
|
||||
occupants_selection_dict: Dict[str, Any] = {}
|
||||
|
|
@ -79,7 +82,10 @@ class TokenService:
|
|||
).data
|
||||
if not build_parts_selection:
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
error_code="",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="User does not have any living space",
|
||||
)
|
||||
|
||||
build_part = build_parts_selection.get(1)
|
||||
|
|
@ -211,7 +217,10 @@ class TokenService:
|
|||
"companies_list": companies_list,
|
||||
}
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
error_code="",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="Creating Token failed...",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -287,11 +296,17 @@ class TokenService:
|
|||
"""Validate request has required token headers."""
|
||||
if not hasattr(request, "headers"):
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
error_code="",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="Request has no headers",
|
||||
)
|
||||
if not request.headers.get(Auth.ACCESS_TOKEN_TAG):
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
error_code="",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="Request has no access token presented",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -322,7 +337,10 @@ class TokenService:
|
|||
return OccupantTokenObject(**redis_object)
|
||||
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
error_code="",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="Unknown user type",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -336,7 +354,10 @@ class TokenService:
|
|||
return cls._process_redis_object(redis_object)
|
||||
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
error_code="",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="Invalid access token",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -350,5 +371,8 @@ class TokenService:
|
|||
return cls._process_redis_object(redis_object)
|
||||
|
||||
raise HTTPExceptionApi(
|
||||
error_code="", lang="en", loc=get_line_number_for_error()
|
||||
error_code="",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="Invalid access token",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from create_routes import get_all_routers
|
|||
from prometheus_fastapi_instrumentator import Instrumentator
|
||||
from app_handler import setup_middleware, get_uvicorn_config
|
||||
from create_file import setup_security_schema, configure_route_security
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
from open_api_creator import OpenAPISchemaCreator, create_openapi_schema
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
|
|
@ -63,12 +63,8 @@ def create_app() -> FastAPI:
|
|||
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,
|
||||
)
|
||||
# Create OpenAPI schema using our custom creator
|
||||
openapi_schema = create_openapi_schema(app)
|
||||
|
||||
# Add security scheme
|
||||
openapi_schema.update(setup_security_schema())
|
||||
|
|
|
|||
|
|
@ -79,7 +79,10 @@ class MiddlewareModule:
|
|||
token_context = TokenService.get_object_via_access_key(access_token=redis_token)
|
||||
if not token_context:
|
||||
raise HTTPExceptionApi(
|
||||
error_code="USER_NOT_FOUND", lang="tr", loc=get_line_number_for_error()
|
||||
error_code="USER_NOT_FOUND",
|
||||
lang="tr",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="TokenService: Token Context couldnt retrieved from redis",
|
||||
)
|
||||
|
||||
return AuthContext(token_context=token_context)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ This module provides functionality to create and customize OpenAPI documentation
|
|||
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from fastapi import FastAPI, APIRouter
|
||||
from fastapi.routing import APIRoute
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
from AllConfigs.main import MainConfig as Config
|
||||
from create_routes import get_all_routers
|
||||
|
||||
|
||||
class OpenAPISchemaCreator:
|
||||
|
|
@ -28,7 +30,7 @@ class OpenAPISchemaCreator:
|
|||
app: FastAPI application instance
|
||||
"""
|
||||
self.app = app
|
||||
self.protected_paths: Set[str] = set()
|
||||
_, self.protected_routes = get_all_routers()
|
||||
self.tags_metadata = self._create_tags_metadata()
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -60,16 +62,16 @@ class OpenAPISchemaCreator:
|
|||
"""
|
||||
return {
|
||||
"Bearer Auth": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "evyos-session-key",
|
||||
"description": "Enter: **'Bearer <JWT>'**, where JWT is the access token",
|
||||
"type": "http",
|
||||
"scheme": "bearer",
|
||||
"bearerFormat": "JWT",
|
||||
"description": "Enter the token with the `Bearer: ` prefix",
|
||||
},
|
||||
"API Key": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "X-API-Key",
|
||||
"description": "API key for service authentication",
|
||||
"description": "Optional API key for service authentication",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -82,20 +84,10 @@ class OpenAPISchemaCreator:
|
|||
"""
|
||||
return {
|
||||
"401": {
|
||||
"description": "Unauthorized - Authentication failed or not provided",
|
||||
"description": "Unauthorized - Invalid or missing credentials",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {"type": "string"},
|
||||
"error_code": {"type": "string"},
|
||||
},
|
||||
},
|
||||
"example": {
|
||||
"detail": "Invalid authentication credentials",
|
||||
"error_code": "INVALID_CREDENTIALS",
|
||||
},
|
||||
"schema": {"$ref": "#/components/schemas/HTTPValidationError"}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -135,6 +127,62 @@ class OpenAPISchemaCreator:
|
|||
},
|
||||
}
|
||||
|
||||
def _process_request_body(self, path: str, method: str, schema: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Process request body to include examples from model config.
|
||||
|
||||
Args:
|
||||
path: Route path
|
||||
method: HTTP method
|
||||
schema: OpenAPI schema to modify
|
||||
"""
|
||||
try:
|
||||
route_schema = schema["paths"][path][method]
|
||||
if "requestBody" in route_schema:
|
||||
request_body = route_schema["requestBody"]
|
||||
if "content" in request_body:
|
||||
content = request_body["content"]
|
||||
if "application/json" in content:
|
||||
json_content = content["application/json"]
|
||||
if "schema" in json_content and "$ref" in json_content["schema"]:
|
||||
ref = json_content["schema"]["$ref"]
|
||||
model_name = ref.split("/")[-1]
|
||||
if model_name in schema["components"]["schemas"]:
|
||||
model_schema = schema["components"]["schemas"][model_name]
|
||||
if "example" in model_schema:
|
||||
json_content["example"] = model_schema["example"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _process_response_examples(self, path: str, method: str, schema: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Process response body to include examples from model config.
|
||||
|
||||
Args:
|
||||
path: Route path
|
||||
method: HTTP method
|
||||
schema: OpenAPI schema to modify
|
||||
"""
|
||||
try:
|
||||
route_schema = schema["paths"][path][method]
|
||||
if "responses" in route_schema:
|
||||
responses = route_schema["responses"]
|
||||
if "200" in responses:
|
||||
response = responses["200"]
|
||||
if "content" in response:
|
||||
content = response["content"]
|
||||
if "application/json" in content:
|
||||
json_content = content["application/json"]
|
||||
if "schema" in json_content and "$ref" in json_content["schema"]:
|
||||
ref = json_content["schema"]["$ref"]
|
||||
model_name = ref.split("/")[-1]
|
||||
if model_name in schema["components"]["schemas"]:
|
||||
model_schema = schema["components"]["schemas"][model_name]
|
||||
if "example" in model_schema:
|
||||
json_content["example"] = model_schema["example"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def configure_route_security(
|
||||
self, path: str, method: str, schema: Dict[str, Any]
|
||||
) -> None:
|
||||
|
|
@ -146,7 +194,8 @@ class OpenAPISchemaCreator:
|
|||
method: HTTP method
|
||||
schema: OpenAPI schema to modify
|
||||
"""
|
||||
if path not in Config.INSECURE_PATHS:
|
||||
# Check if route is protected based on dynamic routing info
|
||||
if path in self.protected_routes and method in self.protected_routes[path]:
|
||||
schema["paths"][path][method]["security"] = [
|
||||
{"Bearer Auth": []},
|
||||
{"API Key": []},
|
||||
|
|
@ -154,6 +203,11 @@ class OpenAPISchemaCreator:
|
|||
schema["paths"][path][method]["responses"].update(
|
||||
self._create_common_responses()
|
||||
)
|
||||
|
||||
# Process request body examples
|
||||
self._process_request_body(path, method, schema)
|
||||
# Process response examples
|
||||
self._process_response_examples(path, method, schema)
|
||||
|
||||
def create_schema(self) -> Dict[str, Any]:
|
||||
"""
|
||||
|
|
@ -174,9 +228,7 @@ class OpenAPISchemaCreator:
|
|||
if "components" not in openapi_schema:
|
||||
openapi_schema["components"] = {}
|
||||
|
||||
openapi_schema["components"][
|
||||
"securitySchemes"
|
||||
] = self._create_security_schemes()
|
||||
openapi_schema["components"]["securitySchemes"] = self._create_security_schemes()
|
||||
|
||||
# Configure route security and responses
|
||||
for route in self.app.routes:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
class HTTPExceptionApi(Exception):
|
||||
|
||||
def __init__(self, error_code: str, lang: str, loc: str = ""):
|
||||
def __init__(self, error_code: str, lang: str, loc: str = "", sys_msg: str = ""):
|
||||
self.error_code = error_code
|
||||
self.lang = lang
|
||||
self.loc = loc
|
||||
self.sys_msg = sys_msg
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ def handle_mongo_errors(func: Callable) -> Callable:
|
|||
lang="en",
|
||||
error_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
|
||||
return wrapper
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ operations and password-related functionality.
|
|||
|
||||
from typing import Any, Dict, Optional
|
||||
from fastapi import HTTPException, status
|
||||
from ApiLibrary.common.line_number import get_line_number_for_error
|
||||
from ErrorHandlers.ErrorHandlers.api_exc_handler import HTTPExceptionApi
|
||||
|
||||
|
||||
|
|
@ -26,9 +27,11 @@ class MongoBaseException(Exception):
|
|||
|
||||
def to_http_exception(self) -> HTTPException:
|
||||
"""Convert to FastAPI HTTPException."""
|
||||
return HTTPExceptionApi(
|
||||
raise HTTPExceptionApi(
|
||||
lang="en",
|
||||
error_code=self.status_code,
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=self.message,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -39,24 +39,28 @@ def handle_mongo_errors(func):
|
|||
error_code="HTTP_503_SERVICE_UNAVAILABLE",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="MongoDB connection failed",
|
||||
)
|
||||
except ServerSelectionTimeoutError:
|
||||
raise HTTPExceptionApi(
|
||||
error_code="HTTP_504_GATEWAY_TIMEOUT",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="MongoDB connection timed out",
|
||||
)
|
||||
except OperationFailure as e:
|
||||
raise HTTPExceptionApi(
|
||||
error_code="HTTP_400_BAD_REQUEST",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
except PyMongoError as e:
|
||||
raise HTTPExceptionApi(
|
||||
error_code="HTTP_500_INTERNAL_SERVER_ERROR",
|
||||
lang="en",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
|
||||
return wrapper
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ class BaseJsonResponse(Generic[T]):
|
|||
lang=cls_object.lang,
|
||||
error_code="HTTP_400_BAD_REQUEST",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=f"Invalid data type: {type(data)}",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -201,6 +202,7 @@ class SinglePostgresResponse(BaseJsonResponse[T]):
|
|||
lang=cls_object.lang,
|
||||
error_code="HTTP_400_BAD_REQUEST",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="No data found",
|
||||
)
|
||||
|
||||
instance = super().__new__(cls)
|
||||
|
|
@ -257,6 +259,7 @@ class AlchemyJsonResponse(BaseJsonResponse[T]):
|
|||
lang=cls_object.lang,
|
||||
error_code="HTTP_400_BAD_REQUEST",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg="No data found",
|
||||
)
|
||||
|
||||
instance = super().__new__(cls)
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ class FilterAttributes:
|
|||
error_code="HTTP_304_NOT_MODIFIED",
|
||||
lang=cls.lang or "tr",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -173,6 +174,7 @@ class FilterAttributes:
|
|||
error_code="HTTP_304_NOT_MODIFIED",
|
||||
lang=cls.lang or "tr",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -193,6 +195,7 @@ class FilterAttributes:
|
|||
error_code="HTTP_304_NOT_MODIFIED",
|
||||
lang=cls.lang or "tr",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -225,6 +228,7 @@ class FilterAttributes:
|
|||
error_code="HTTP_304_NOT_MODIFIED",
|
||||
lang=cls.lang or "tr",
|
||||
loc=get_line_number_for_error(),
|
||||
sys_msg=str(e),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
Loading…
Reference in New Issue