updated handler exceptions

This commit is contained in:
berkay 2025-01-11 19:40:10 +03:00
parent cecf1e69a2
commit 56b693989d
34 changed files with 571 additions and 1126 deletions

View File

@ -5,9 +5,11 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi import Request, HTTPException, status from fastapi import Request, HTTPException, status
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from api_objects.errors.errorMessages import EXCEPTION_DICTS, ERRORS_DICT, ERRORS_LANG
from api_objects.errors.errorHandlers import HTTPExceptionEvyos, HTTPExceptionAnyHandler, HTTPExceptionEvyosHandler
from middlewares.token_middleware import AuthHeaderMiddleware from middlewares.token_middleware import AuthHeaderMiddleware
from application.create_file import create_app from application.create_file import create_app
from api_objects.errors.errors_dictionary import ErrorHandlers
from prometheus_fastapi_instrumentator import Instrumentator from prometheus_fastapi_instrumentator import Instrumentator
app = create_app(routers=routers) app = create_app(routers=routers)
@ -24,17 +26,20 @@ app.add_middleware(
) )
app.add_middleware(AuthHeaderMiddleware) app.add_middleware(AuthHeaderMiddleware)
# Initialize error handlers # Initialize Exception and ExceptionInstance handlers
error_handlers = ErrorHandlers.create( CustomExceptionHandler = HTTPExceptionEvyosHandler(**dict(
requests=Request, statuses=status,
exceptions=HTTPException, exceptions=HTTPException,
response_model=JSONResponse, response_model=JSONResponse,
status=status, exceptions_dict=EXCEPTION_DICTS,
) errors_dict=ERRORS_DICT,
error_language_dict=ERRORS_LANG
))
CustomExceptionAnyHandler = HTTPExceptionAnyHandler(response_model=JSONResponse)
# Register error handlers with bound methods # Register error handlers with bound methods
app.add_exception_handler(HTTPException, error_handlers.exception_handler_http) app.add_exception_handler(HTTPExceptionEvyos, CustomExceptionHandler.handle_exception)
app.add_exception_handler(Exception, error_handlers.exception_handler_exception) app.add_exception_handler(Exception, CustomExceptionAnyHandler.any_exception_handler)
if __name__ == "__main__": if __name__ == "__main__":
uvicorn_config = { uvicorn_config = {

View File

@ -13,7 +13,7 @@ from api_services.templates.password_templates import (
from api_services.token_service import TokenService from api_services.token_service import TokenService
from api_services.redis.functions import RedisActions from api_services.redis.functions import RedisActions
from api_library.response_handlers import ResponseHandler from api_library.response_handlers import ResponseHandler
from api_library.date_time_actions.date_functions import system_arrow from api_library.date_time_actions.date_functions import system_arrow, DateTimeLocal
# from api_library.user_logger import UserLogger # from api_library.user_logger import UserLogger
@ -74,38 +74,41 @@ class AuthenticationLoginEventMethods(MethodToEvent):
@classmethod @classmethod
def authentication_login_with_domain_and_creds(cls, data: Login, request: Request): def authentication_login_with_domain_and_creds(cls, data: Login, request: Request):
try: from api_objects import HTTPExceptionEvyos
access_dict = Users.login_user_with_credentials(data=data, request=request)
found_user = access_dict.get("user")
if not found_user:
# UserLogger.log_login_attempt(
# request,
# None,
# data.domain,
# data.access_key,
# success=False,
# error="Invalid credentials",
# )
return ResponseHandler.unauthorized("Invalid credentials")
raise HTTPExceptionEvyos(
error_code="UNKNOWN_ERROR",
lang="en",
)
access_dict = Users.login_user_with_credentials(data=data, request=request)
found_user = access_dict.get("user")
Users.client_arrow = DateTimeLocal(
is_client=True, timezone=found_user.local_timezone
)
if not found_user:
# UserLogger.log_login_attempt( # UserLogger.log_login_attempt(
# request, found_user.id, data.domain, data.access_key, success=True # request,
# None,
# data.domain,
# data.access_key,
# success=False,
# error="Invalid credentials",
# ) # )
response_data = { return ResponseHandler.unauthorized("Invalid credentials")
return ResponseHandler.success(
message="User logged in successfully",
data={
"access_token": access_dict.get("access_token"), "access_token": access_dict.get("access_token"),
"refresh_token": access_dict.get("refresher_token"), "refresh_token": access_dict.get("refresher_token"),
"access_object": access_dict.get("access_object"), "access_object": access_dict.get("access_object"),
"user": found_user.get_dict(), "user": found_user.get_dict(),
} },
return ResponseHandler.success( )
message="User logged in successfully", # except Exception as e:
data=response_data, # # UserLogger.log_login_attempt(
) # # request, None, data.domain, data.access_key, success=False, error=str(e)
except Exception as e: # # )
# UserLogger.log_login_attempt( # raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e))
# request, None, data.domain, data.access_key, success=False, error=str(e)
# )
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e))
class AuthenticationSelectEventMethods(MethodToEvent): class AuthenticationSelectEventMethods(MethodToEvent):
@ -125,6 +128,9 @@ class AuthenticationSelectEventMethods(MethodToEvent):
cls, data: EmployeeSelection, token_dict: EmployeeTokenObject, request: Request cls, data: EmployeeSelection, token_dict: EmployeeTokenObject, request: Request
): ):
"""Handle employee company selection""" """Handle employee company selection"""
Users.client_arrow = DateTimeLocal(
is_client=True, timezone=token_dict.local_timezone
)
if data.company_uu_id not in token_dict.companies_uu_id_list: if data.company_uu_id not in token_dict.companies_uu_id_list:
return ResponseHandler.unauthorized( return ResponseHandler.unauthorized(
"Company not found in user's company list" "Company not found in user's company list"

View File

@ -79,7 +79,7 @@ def endpoint_restriction_available(request: Request, data: CheckEndpointAccess):
system=True, system=True,
).data ).data
if not endpoint: if not endpoint:
EndpointRestriction.raise_http_exception( raise EndpointRestriction.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message="Only Occupant can see this data", message="Only Occupant can see this data",
@ -94,7 +94,7 @@ def endpoint_restriction_available(request: Request, data: CheckEndpointAccess):
== token_dict.selected_occupant.living_space_id, == token_dict.selected_occupant.living_space_id,
).data ).data
if not event_occupant: if not event_occupant:
EndpointRestriction.raise_http_exception( raise EndpointRestriction.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message="This endpoint is not available for this occupant", message="This endpoint is not available for this occupant",
@ -110,7 +110,7 @@ def endpoint_restriction_available(request: Request, data: CheckEndpointAccess):
Event2Employee.employee_id == token_dict.selected_company.employee_id, Event2Employee.employee_id == token_dict.selected_company.employee_id,
).data ).data
if not event_employee: if not event_employee:
EndpointRestriction.raise_http_exception( raise EndpointRestriction.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message="This endpoint is not available for this employee", message="This endpoint is not available for this employee",

View File

@ -10,6 +10,7 @@ from application.create_file import create_app
from api_objects.errors.errors_dictionary import ErrorHandlers from api_objects.errors.errors_dictionary import ErrorHandlers
from prometheus_fastapi_instrumentator import Instrumentator from prometheus_fastapi_instrumentator import Instrumentator
app = create_app(routers=routers) app = create_app(routers=routers)
Instrumentator().instrument(app=app).expose(app=app) Instrumentator().instrument(app=app).expose(app=app)

View File

@ -84,6 +84,10 @@ class AuthActions:
person_uu_id=str(found_user.person.uu_id), person_uu_id=str(found_user.person.uu_id),
request=dict(request.headers), request=dict(request.headers),
available_occupants=occupants_selection_dict, available_occupants=occupants_selection_dict,
timezone=(
found_user.local_timezone if found_user.local_timezone else "GMT+0"
),
lang=found_user.lang if found_user.lang else "tr",
), ),
) )
return dict( return dict(
@ -136,6 +140,7 @@ class AuthActions:
company_address=company_address, company_address=company_address,
) )
) )
AccessObjectActions.save_object_to_redis( AccessObjectActions.save_object_to_redis(
access_token=access_token, access_token=access_token,
model_object=EmployeeTokenObject( model_object=EmployeeTokenObject(
@ -151,6 +156,10 @@ class AuthActions:
companies_id_list=companies_id_list, companies_id_list=companies_id_list,
duty_uu_id_list=duty_uu_id_list, duty_uu_id_list=duty_uu_id_list,
duty_id_list=duty_id_list, duty_id_list=duty_id_list,
timezone=(
found_user.local_timezone if found_user.local_timezone else "GMT+0"
),
lang=found_user.lang if found_user.lang else "tr",
), ),
) )
return dict( return dict(
@ -185,11 +194,30 @@ class AuthActions:
found_user.generate_access_token() if not access_token else access_token found_user.generate_access_token() if not access_token else access_token
) )
# Prepare the user's details to save in Redis Session # Prepare the user's details to save in Redis Session
if found_user.is_occupant: # Check if user is NOT an occupant if found_user.is_occupant: # Check if user is an occupant
return cls.do_occupant_login_token( cls.do_occupant_login_token(request, found_user, domain, access_token)
return {
"access_object": cls.do_occupant_login_token(
request, found_user, domain, access_token
),
"access_token": access_token,
"refresher_token": (
found_user.generate_refresh_token()
if found_user.remember_me
else None
),
}
cls.do_employee_login_token(request, found_user, domain, access_token)
return {
"access_object": cls.do_employee_login_token(
request, found_user, domain, access_token request, found_user, domain, access_token
) ),
return cls.do_employee_login_token(request, found_user, domain, access_token) "access_token": access_token,
"refresher_token": (
found_user.generate_refresh_token() if found_user.remember_me else None
),
}
@classmethod @classmethod
def update_selected_to_redis(cls, request, add_payload): def update_selected_to_redis(cls, request, add_payload):

View File

@ -24,6 +24,7 @@ class AccessObjectActions:
Raises: Raises:
HTTPException: If save fails HTTPException: If save fails
""" """
try: try:
RedisActions.save_object_to_redis( RedisActions.save_object_to_redis(
access_token=access_token, access_token=access_token,

View File

@ -1,8 +1,100 @@
from typing import Any, Union from typing import Any, Union, Callable, Any
from fastapi import status from fastapi import status
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from pydantic import BaseModel
from api_validations.validations_request import PydanticBaseModel, BaseModelRegular
from databases.sql_models.response_model import AlchemyResponse from databases.sql_models.response_model import AlchemyResponse
from sqlalchemy.orm import Query
MODEL_TYPE = Callable[[Any], Any]
class Pagination:
size: int = 10
page: int = 1
orderField: str = "id"
orderType: str = "asc"
pageCount: int = 1
totalCount: int = 1
totalPage: int = 1
def change(self, page=None, size=None, order_field=None, order_type=None):
self.page = page or self.page
self.size = size or self.size
self.orderField = order_field or self.orderField
self.orderType = order_type or self.orderType
self.setter_page()
def feed(self, data):
if isinstance(data, list):
self.totalCount = len(data)
elif isinstance(data, AlchemyResponse):
self.totalCount = data.count
elif isinstance(data, Query):
self.totalCount = data.count()
def setter_page(self):
self.pageCount = self.size
self.totalPage = int(round(self.totalCount / self.size, 0))
if self.totalCount % self.size > 0:
if self.page == self.totalPage:
self.pageCount = self.totalCount % self.size
self.totalPage = int(round(self.totalCount / self.size, 0)) + 1
def as_dict(self):
return {
"size": self.size,
"page": self.page,
"totalCount": self.totalCount,
"totalPage": self.totalPage,
"pageCount": self.pageCount,
"orderField": self.orderField,
"orderType": self.orderType,
}
class SingleAlchemyResponse:
status_code = "HTTP_200_OK"
result: AlchemyResponse
response_model: MODEL_TYPE
message: str
completed: bool
def __new__(
cls,
message: str,
response_model: MODEL_TYPE,
status_code: str = "HTTP_200_OK",
result: AlchemyResponse = None,
completed: bool = True,
):
cls.status_code = getattr(status, status_code, "HTTP_200_OK")
cls.message = message
cls.result = result
cls.completed = completed
cls.response_model = response_model
if not isinstance(cls.result, AlchemyResponse):
raise Exception("Invalid response type 4 single alchemy response")
if not cls.result.first:
raise Exception("Invalid data type 4 single alchemy response")
pagination = Pagination()
pagination.change(page=1)
BaseModelRegular(**cls.result.data.get_dict())
data = cls.result.data.get_dict()
if cls.response_model:
data = cls.response_model(**cls.result.data.get_dict()).dump()
return JSONResponse(
status_code=cls.status_code,
content=dict(
pagination=pagination.as_dict(),
completed=cls.completed,
message=cls.message,
data=data,
),
)
class AlchemyJsonResponse: class AlchemyJsonResponse:
@ -11,27 +103,16 @@ class AlchemyJsonResponse:
result: AlchemyResponse result: AlchemyResponse
completed: bool completed: bool
filter_attributes: Any = None filter_attributes: Any = None
response_model: Any = None response_model: MODEL_TYPE = None
cls_object: Any = None cls_object: Any = None
@staticmethod
def get_total_count(cls_object, filter_attributes):
total_page_number = 1
count_to_use = cls_object.total_count / int(filter_attributes.size)
if cls_object.total_count > int(filter_attributes.size):
if isinstance(count_to_use, int):
total_page_number = round(count_to_use, 0)
elif isinstance(count_to_use, float):
total_page_number = round(count_to_use, 0) + 1
return total_page_number
def __new__( def __new__(
cls, cls,
message: str, message: str,
status_code: str = "HTTP_200_OK", status_code: str = "HTTP_200_OK",
result: Union[Any, list] = None, result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
completed: bool = True, completed: bool = True,
response_model: Any = None, response_model: MODEL_TYPE = None,
cls_object: Any = None, cls_object: Any = None,
filter_attributes: Any = None, filter_attributes: Any = None,
): ):
@ -40,108 +121,121 @@ class AlchemyJsonResponse:
cls.result = result cls.result = result
cls.completed = completed cls.completed = completed
cls.response_model = response_model cls.response_model = response_model
cls.filter_attributes = filter_attributes
pagination_dict = { cls.cls_object = cls_object
"size/total_count": [10, 10], pagination = Pagination()
"page/total_page": [1, 1],
"order_field": "id",
"order_type": "asc",
}
if filter_attributes:
total_page_number = cls.get_total_count(cls_object, filter_attributes)
pagination_dict = {
"size/total_count": [filter_attributes.size, cls_object.total_count],
"page/total_page": [filter_attributes.page, total_page_number],
"order_field": filter_attributes.order_field,
"order_type": filter_attributes.order_type,
}
if isinstance(cls.result, dict) or isinstance(cls.result, list):
return JSONResponse(
status_code=cls.status_code,
content=dict(
total_count=len(cls.result),
count=len(cls.result),
pagination=pagination_dict,
completed=cls.completed,
message=cls.message,
data=cls.result,
),
)
first_item = getattr(cls.result, "data", None)
if not first_item:
return JSONResponse(
status_code=cls.status_code,
content=dict(
total_count=0,
count=0,
pagination=pagination_dict,
completed=cls.completed,
message=cls.message,
data=[],
),
)
if cls.result.first: if cls.result.first:
return JSONResponse( raise Exception("Invalid data type 4 alchemy response")
status_code=cls.status_code,
content=dict(
total_count=1,
count=1,
pagination=pagination_dict,
completed=cls.completed,
message=cls.message,
data=cls.result.data.get_dict(),
),
)
if not cls.result.get(1).filter_attr and isinstance(cls.result.data, list): if filter_attributes:
counts = cls.result.count pagination.change(
return JSONResponse( page=filter_attributes.page, size=filter_attributes.size,
status_code=cls.status_code, order_field=filter_attributes.order_field,
content=dict( order_type=filter_attributes.order_type,
total_count=counts,
count=counts,
pagination=pagination_dict,
completed=cls.completed,
message=cls.message,
data=[result_data.get_dict() for result_data in cls.result.data],
),
) )
# filter_model = cls.result.get(1).filter_attr
total_count = cls.result.get(1).query.limit(None).offset(None).count()
total_page_number = cls.get_total_count(cls_object, filter_attributes)
pagination_dict = {
"size/total_count": [filter_attributes.size, cls_object.total_count],
"page/total_page": [filter_attributes.page, total_page_number],
"order_field": filter_attributes.order_field,
"order_type": filter_attributes.order_type,
}
include_joins = dict(
include_joins=(
filter_attributes.include_joins
if filter_attributes.include_joins
else []
)
)
data = [] data = []
for data_object in cls.result.data: for data_object in cls.result.data:
data_dict = data_object.get_dict(include_joins=include_joins) data_dict = data_object.get_dict()
if cls.response_model: if cls.response_model:
data_dict = cls.response_model( data_dict = cls.response_model(**data_object.get_dict()).dump()
**data_object.get_dict(include_joins=include_joins)
).dump()
data.append(data_dict) data.append(data_dict)
pagination.feed(data)
return JSONResponse( return JSONResponse(
status_code=cls.status_code, status_code=cls.status_code,
content=dict( content=dict(
total_count=total_count or 1, pagination=pagination.as_dict(),
count=cls.result.count,
pagination=pagination_dict,
message=cls.message, message=cls.message,
completed=cls.completed, completed=cls.completed,
data=data, data=data,
), ),
) )
class ListJsonResponse:
status_code = "HTTP_200_OK"
result: list
message: str
completed: bool
filter_attributes: Any
response_model: MODEL_TYPE = None,
cls_object: Any = None,
def __new__(
cls,
message: str,
status_code: str = "HTTP_200_OK",
result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
completed: bool = True,
response_model: MODEL_TYPE = None,
cls_object: Any = None,
filter_attributes: Any = None,
):
cls.status_code = getattr(status, status_code, "HTTP_200_OK")
cls.message = message
cls.result = result
cls.completed = completed
cls.filter_attributes = filter_attributes
cls.response_model: MODEL_TYPE = response_model
if not isinstance(cls.result, list):
raise Exception("Invalid data type 4 list json response")
pagination = Pagination()
pagination.change(page=1)
data = list(cls.result)
if cls.response_model:
data = [cls.response_model(**data_object).dump() for data_object in cls.result]
pagination.feed(data)
return JSONResponse(
status_code=cls.status_code,
content=dict(
pagination=pagination.as_dict(),
completed=cls.completed,
message=cls.message,
data=cls.result,
),
)
class DictJsonResponse:
status_code = "HTTP_200_OK"
result: dict
message: str
completed: bool
filter_attributes: Any
response_model: MODEL_TYPE = None,
cls_object: Any = None,
def __new__(
cls,
message: str,
status_code: str = "HTTP_200_OK",
result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None,
completed: bool = True,
response_model: MODEL_TYPE = None,
cls_object: Any = None,
filter_attributes: Any = None,
):
cls.status_code = getattr(status, status_code, "HTTP_200_OK")
cls.message = message
cls.result = result
cls.completed = completed
cls.filter_attributes = filter_attributes
cls.response_model: MODEL_TYPE = response_model
if not isinstance(cls.result, dict):
raise Exception("Invalid data type 4 dict json response")
pagination = Pagination()
pagination.change(page=1)
data = cls.result
if cls.response_model:
data = cls.response_model(**cls.result).dump()
return JSONResponse(
status_code=cls.status_code,
content=dict(
pagination=pagination.as_dict(),
completed=cls.completed,
message=cls.message,
data=data,
),
)

View File

@ -13,6 +13,12 @@ services:
- auth_venv:/service_app/.venv - auth_venv:/service_app/.venv
- auth_logs:/service_app/logs - auth_logs:/service_app/logs
# wag_management_init_service:
# container_name: wag_management_init_service
# build:
# context: .
# dockerfile: service_app_init/Dockerfile
# wag_management_event_service: # wag_management_event_service:
# container_name: wag_management_event_service # container_name: wag_management_event_service
# # restart: on-failure # # restart: on-failure
@ -41,11 +47,7 @@ services:
# - validation_venv:/service_app/.venv # - validation_venv:/service_app/.venv
# - validation_logs:/service_app/logs # - validation_logs:/service_app/logs
# wag_management_init_service:
# container_name: wag_management_init_service
# build:
# context: .
# dockerfile: service_app_init/Dockerfile
volumes: volumes:
auth_venv: auth_venv:

View File

@ -14,20 +14,6 @@ from api_events.events.address.address import (
AddressPostCodeUpdateEventMethod, AddressPostCodeUpdateEventMethod,
AddressPostCodeListEventMethod, AddressPostCodeListEventMethod,
) )
from api_events.events.application.authentication import (
AuthenticationLoginEventMethod,
AuthenticationSelectEventMethod,
AuthenticationCheckTokenEventMethod,
AuthenticationRefreshEventMethod,
AuthenticationChangePasswordEventMethod,
AuthenticationCreatePasswordEventMethod,
AuthenticationResetPasswordEventMethod,
AuthenticationDisconnectUserEventMethod,
AuthenticationLogoutEventMethod,
AuthenticationRefreshTokenEventMethod,
AuthenticationForgotPasswordEventMethod,
AuthenticationDownloadAvatarEventMethod,
)
from api_events.events.account.account_records import ( from api_events.events.account.account_records import (
AccountRecordsListEventMethod, AccountRecordsListEventMethod,
AccountRecordsCreateEventMethod, AccountRecordsCreateEventMethod,
@ -184,18 +170,6 @@ __all__ = [
"PeopleUpdateEventMethod", "PeopleUpdateEventMethod",
"PeoplePatchEventMethod", "PeoplePatchEventMethod",
"PeopleCreateEventMethod", "PeopleCreateEventMethod",
"AuthenticationLoginEventMethod",
"AuthenticationSelectEventMethod",
"AuthenticationCheckTokenEventMethod",
"AuthenticationRefreshEventMethod",
"AuthenticationChangePasswordEventMethod",
"AuthenticationCreatePasswordEventMethod",
"AuthenticationResetPasswordEventMethod",
"AuthenticationDisconnectUserEventMethod",
"AuthenticationLogoutEventMethod",
"AuthenticationRefreshTokenEventMethod",
"AuthenticationForgotPasswordEventMethod",
"AuthenticationDownloadAvatarEventMethod",
"AccountRecordsListEventMethod", "AccountRecordsListEventMethod",
"AccountRecordsCreateEventMethod", "AccountRecordsCreateEventMethod",
"AccountRecordsUpdateEventMethod", "AccountRecordsUpdateEventMethod",

View File

@ -64,7 +64,7 @@ class AccountRecordsListEventMethods(MethodToEvent):
token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject], token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject],
): ):
if not isinstance(token_dict, OccupantTokenObject): if not isinstance(token_dict, OccupantTokenObject):
raise AccountRecords().raise_http_exception( raise AccountRecords.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message="Only Occupant can see this data", message="Only Occupant can see this data",
@ -76,7 +76,7 @@ class AccountRecordsListEventMethods(MethodToEvent):
id=token_dict.selected_occupant.living_space_id id=token_dict.selected_occupant.living_space_id
).data ).data
if not living_space: if not living_space:
raise AccountRecords().raise_http_exception( raise AccountRecords.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message="Living space not found", message="Living space not found",
@ -214,7 +214,7 @@ class AccountRecordsCreateEventMethods(MethodToEvent):
BuildIbans.build_id == token_dict.selected_occupant.build_id, BuildIbans.build_id == token_dict.selected_occupant.build_id,
).data ).data
if not build_iban: if not build_iban:
BuildIbans.raise_http_exception( raise BuildIbans.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"{data.iban} is not found in company related to your organization", message=f"{data.iban} is not found in company related to your organization",

View File

@ -1,5 +0,0 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
class DecisionBookEvents(MethodToEvent): ...

View File

@ -1,5 +0,0 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
class ApplicationEvents(MethodToEvent): ...

View File

@ -1,750 +0,0 @@
import json
from typing import Union
from fastapi import status
from fastapi.requests import Request
from fastapi.exceptions import HTTPException
from api_objects import OccupantTokenObject, EmployeeTokenObject
from api_objects.auth.token_objects import CompanyToken, OccupantToken
from api_services.templates.password_templates import (
password_is_changed_template,
change_your_password_template,
)
from api_services.token_service import TokenService
from api_services.redis.functions import RedisActions
from api_library.response_handlers import ResponseHandler
from api_library.date_time_actions.date_functions import system_arrow
# from api_library.user_logger import UserLogger
from api_validations.validations_request import (
Login,
Logout,
ChangePassword,
EmployeeSelection,
OccupantSelection,
CreatePassword,
Forgot,
# ResetPassword,
# RefreshToken,
)
from api_validations.validations_response import (
AuthenticationLoginResponse,
AuthenticationRefreshResponse,
AuthenticationUserInfoResponse,
)
from ApiServices.api_handlers.auth_actions.auth import AuthActions
from api_configs import Auth, ApiStatic
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from databases import (
Companies,
Staff,
Duties,
Departments,
Employees,
BuildLivingSpace,
BuildParts,
Build,
Duty,
Event2Occupant,
Event2Employee,
Users,
UsersTokens,
OccupantTypes,
RelationshipEmployee2Build,
)
from api_services import (
send_email,
)
class AuthenticationLoginEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Login via domain and access key : [email] | [phone]"
event_category = "AUTHENTICATION"
__event_keys__ = {
"e672846d-cc45-4d97-85d5-6f96747fac67": "authentication_login_with_domain_and_creds",
}
__event_validation__ = {
"e672846d-cc45-4d97-85d5-6f96747fac67": AuthenticationLoginResponse,
}
@classmethod
def authentication_login_with_domain_and_creds(cls, data: Login, request: Request):
try:
access_dict = Users.login_user_with_credentials(data=data, request=request)
found_user = access_dict.get("user")
if not found_user:
# UserLogger.log_login_attempt(
# request,
# None,
# data.domain,
# data.access_key,
# success=False,
# error="Invalid credentials",
# )
return ResponseHandler.unauthorized("Invalid credentials")
# UserLogger.log_login_attempt(
# request, found_user.id, data.domain, data.access_key, success=True
# )
response_data = {
"access_token": access_dict.get("access_token"),
"refresh_token": access_dict.get("refresher_token"),
"access_object": access_dict.get("access_object"),
"user": found_user.get_dict(),
}
return ResponseHandler.success(
message="User logged in successfully",
data=response_data,
)
except Exception as e:
# UserLogger.log_login_attempt(
# request, None, data.domain, data.access_key, success=False, error=str(e)
# )
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e))
class AuthenticationSelectEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Select Employee Duty or Occupant Type"
event_category = "AUTHENTICATION"
__event_keys__ = {
"cee96b9b-8487-4e9f-aaed-2e8c79687bf9": "authentication_select_company_or_occupant_type",
}
__event_validation__ = {
"cee96b9b-8487-4e9f-aaed-2e8c79687bf9": "authentication_select_company_or_occupant_type",
}
@classmethod
def _handle_employee_selection(
cls, data: EmployeeSelection, token_dict: EmployeeTokenObject, request: Request
):
"""Handle employee company selection"""
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")
@classmethod
def _handle_occupant_selection(
cls, data: OccupantSelection, token_dict: OccupantTokenObject, request: Request
):
"""Handle occupant type selection"""
# Get occupant type
occupant_type = OccupantTypes.filter_by_one(
system=True, uu_id=data.occupant_uu_id
).data
if not occupant_type:
return ResponseHandler.not_found("Occupant Type not found")
# Get build part
build_part = BuildParts.filter_by_one(
system=True, uu_id=data.build_part_uu_id
).data
if not build_part:
return ResponseHandler.not_found("Build Part not found")
# Get build and company info
build = Build.filter_one(Build.id == build_part.build_id).data
related_company = RelationshipEmployee2Build.filter_one(
RelationshipEmployee2Build.member_id == build.id
).data
company_related = Companies.filter_one(
Companies.id == related_company.company_id
).data
responsible_employee = Employees.filter_one(
Employees.id == related_company.employee_id
).data
# Get selected occupant type
selected_occupant_type = BuildLivingSpace.filter_one(
BuildLivingSpace.occupant_type == occupant_type.id,
BuildLivingSpace.person_id == token_dict.person_id,
BuildLivingSpace.build_parts_id == build_part.id,
).data
if not selected_occupant_type:
return ResponseHandler.not_found("Selected occupant type not found")
# Get reachable events
reachable_event_list_id = Event2Occupant.get_event_id_by_build_living_space_id(
build_living_space_id=selected_occupant_type.id
)
# Create occupant token
occupant_token = OccupantToken(
living_space_id=selected_occupant_type.id,
living_space_uu_id=selected_occupant_type.uu_id.__str__(),
occupant_type_id=occupant_type.id,
occupant_type_uu_id=occupant_type.uu_id.__str__(),
occupant_type=occupant_type.occupant_type,
build_id=build.id,
build_uuid=build.uu_id.__str__(),
build_part_id=build_part.id,
build_part_uuid=build_part.uu_id.__str__(),
responsible_employee_id=responsible_employee.id,
responsible_employee_uuid=responsible_employee.uu_id.__str__(),
responsible_company_id=company_related.id,
responsible_company_uuid=company_related.uu_id.__str__(),
reachable_event_list_id=reachable_event_list_id,
)
# Update Redis
AuthActions.update_selected_to_redis(
request=request, add_payload=occupant_token
)
return ResponseHandler.success("Occupant selected successfully")
@classmethod
def authentication_select_company_or_occupant_type(
cls,
request: Request,
data: Union[EmployeeSelection, OccupantSelection],
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
):
"""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
)
class AuthenticationCheckTokenEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Check Token is valid for user"
event_category = "AUTHENTICATION"
__event_keys__ = {
"73d77e45-a33f-4f12-909e-3b56f00d8a12": "authentication_check_token_is_valid",
}
__event_validation__ = {
"73d77e45-a33f-4f12-909e-3b56f00d8a12": "authentication_check_token_is_valid",
}
@classmethod
def authentication_check_token_is_valid(cls, request: Request):
try:
if RedisActions.get_object_via_access_key(request=request):
return ResponseHandler.success("Access Token is valid")
except HTTPException:
return ResponseHandler.unauthorized("Access Token is NOT valid")
class AuthenticationRefreshEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Refresh user info using access token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"48379bb2-ba81-4d8e-a9dd-58837cfcbf67": "authentication_refresh_user_info",
}
__event_validation__ = {
"48379bb2-ba81-4d8e-a9dd-58837cfcbf67": AuthenticationRefreshResponse,
}
@classmethod
def authentication_refresh_user_info(
cls,
request: Request,
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
):
try:
access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG)
if not access_token:
return ResponseHandler.unauthorized()
# Get user and token info
found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data
if not found_user:
return ResponseHandler.not_found("User not found")
user_token = UsersTokens.filter_one(
UsersTokens.domain == found_user.domain_name,
UsersTokens.user_id == found_user.id,
UsersTokens.token_type == "RememberMe",
).data
response_data = {
"access_token": access_token,
"refresh_token": getattr(user_token, "token", None),
"user": found_user.get_dict(),
}
return ResponseHandler.success(
"User info refreshed successfully",
data=response_data,
)
except Exception as e:
return ResponseHandler.error(str(e))
class AuthenticationChangePasswordEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Change password with access token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"f09f7c1a-bee6-4e32-8444-962ec8f39091": "authentication_change_password",
}
__event_validation__ = {
"f09f7c1a-bee6-4e32-8444-962ec8f39091": "authentication_change_password",
}
@classmethod
def authentication_change_password(
cls,
request: Request,
data: ChangePassword,
token_dict: Union[EmployeeTokenObject, OccupantTokenObject],
):
try:
if not isinstance(token_dict, EmployeeTokenObject):
return ResponseHandler.unauthorized(
"Only employees can change password"
)
found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data
if not found_user:
return ResponseHandler.not_found("User not found")
if not found_user.check_password(data.old_password):
# UserLogger.log_password_change(
# request,
# found_user.id,
# "change",
# success=False,
# error="Invalid old password",
# )
return ResponseHandler.unauthorized("Old password is incorrect")
found_user.set_password(data.new_password)
# UserLogger.log_password_change(
# request, found_user.id, "change", success=True
# )
return ResponseHandler.success("Password changed successfully")
except Exception as e:
# UserLogger.log_password_change(
# request,
# found_user.id if found_user else None,
# "change",
# success=False,
# error=str(e),
# )
return ResponseHandler.error(str(e))
class AuthenticationCreatePasswordEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Create password with password reset token requested via email"
event_category = "AUTHENTICATION"
__event_keys__ = {
"c519f9af-92e1-47b2-abf7-5a3316d075f7": "authentication_create_password",
}
__event_validation__ = {
"c519f9af-92e1-47b2-abf7-5a3316d075f7": "authentication_create_password",
}
@classmethod
def authentication_create_password(cls, data: CreatePassword):
if not data.re_password == data.password:
raise HTTPException(
status_code=status.HTTP_406_NOT_ACCEPTABLE, detail="Password must match"
)
if found_user := Users.filter_one(
Users.password_token == data.password_token
).data:
found_user.create_password(found_user=found_user, password=data.password)
found_user.password_token = ""
found_user.save()
send_email_completed = send_email(
subject=f"Dear {found_user.user_tag}, your password has been changed.",
receivers=[str(found_user.email)],
html=password_is_changed_template(user_name=found_user.user_tag),
)
if not send_email_completed:
raise HTTPException(
status_code=400, detail="Email can not be sent. Try again later"
)
return ResponseHandler.success(
"Password is created successfully",
data=found_user.get_dict(),
)
return ResponseHandler.not_found("Record not found")
class AuthenticationDisconnectUserEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Disconnect all sessions of user in access token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"8b586848-2fb3-4161-abbe-642157eec7ce": "authentication_disconnect_user",
}
__event_validation__ = {
"8b586848-2fb3-4161-abbe-642157eec7ce": "authentication_disconnect_user",
}
@classmethod
def authentication_disconnect_user(
cls, data: Logout, token_dict: Union[EmployeeTokenObject, OccupantTokenObject]
):
found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data
if not found_user:
return ResponseHandler.not_found("User not found")
if already_tokens := RedisActions.get_object_via_user_uu_id(
user_id=str(found_user.uu_id)
):
for key, token_user in already_tokens.items():
RedisActions.delete(key)
selected_user = Users.filter_one(
Users.uu_id == token_user.get("uu_id"),
).data
selected_user.remove_refresher_token(
domain=data.domain, disconnect=True
)
return ResponseHandler.success(
"All sessions are disconnected",
data=selected_user.get_dict(),
)
return ResponseHandler.not_found("Invalid data")
class AuthenticationLogoutEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Logout only single session of user which domain is provided"
event_category = "AUTHENTICATION"
__event_keys__ = {
"5cc22e4e-a0f7-4077-be41-1871feb3dfd1": "authentication_logout_user",
}
__event_validation__ = {
"5cc22e4e-a0f7-4077-be41-1871feb3dfd1": "authentication_logout_user",
}
@classmethod
def authentication_logout_user(
cls, request: Request, data: Logout, token_dict: dict = None
):
token_user = None
if already_tokens := RedisActions.get_object_via_access_key(request=request):
for key in already_tokens:
token_user = RedisActions.get_json(key)
if token_user.get("domain") == data.domain:
RedisActions.delete(key)
selected_user = Users.filter_one(
Users.uu_id == token_user.get("uu_id"),
).data
selected_user.remove_refresher_token(domain=data.domain)
return ResponseHandler.success(
"Session is logged out",
data=token_user,
)
return ResponseHandler.not_found("Logout is not successfully completed")
class AuthenticationRefreshTokenEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Refresh access token with refresher token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"c90f3334-10c9-4181-b5ff-90d98a0287b2": "authentication_refresher_token",
}
__event_validation__ = {
"c90f3334-10c9-4181-b5ff-90d98a0287b2": AuthenticationRefreshResponse,
}
@classmethod
def authentication_refresher_token(
# cls, request: Request, data: RefreshToken, token_dict: dict = None
cls,
request: Request,
data,
token_dict: dict = None,
):
token_refresher = UsersTokens.filter_by_one(
token=data.refresh_token,
domain=data.domain,
**UsersTokens.valid_record_dict,
).data
if not token_refresher:
return ResponseHandler.not_found("Invalid data")
if found_user := Users.filter_one(
Users.id == token_refresher.user_id,
).data:
found_user: Users = found_user
access_key = AuthActions.save_access_token_to_redis(
request=request, found_user=found_user, domain=data.domain
)
found_user.last_agent = request.headers.get("User-Agent", None)
found_user.last_platform = request.headers.get("Origin", None)
found_user.last_remote_addr = getattr(
request, "remote_addr", None
) or request.headers.get("X-Forwarded-For", None)
found_user.last_seen = str(system_arrow.now())
response_data = {
"access_token": access_key,
"refresh_token": data.refresh_token,
}
return ResponseHandler.success(
"User is logged in successfully via refresher token",
data=response_data,
)
return ResponseHandler.not_found("Invalid data")
class AuthenticationForgotPasswordEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Send an email to user for a valid password reset token"
event_category = "AUTHENTICATION"
__event_keys__ = {
"e3ca6e24-b9f8-4127-949c-3bfa364e3513": "authentication_forgot_password",
}
__event_validation__ = {
"e3ca6e24-b9f8-4127-949c-3bfa364e3513": "authentication_forgot_password",
}
@classmethod
def authentication_forgot_password(
cls,
request: Request,
data: Forgot,
):
found_user: Users = Users.check_user_exits(
access_key=data.access_key, domain=data.domain
)
forgot_key = AuthActions.save_access_token_to_redis(
request=request, found_user=found_user, domain=data.domain
)
forgot_link = ApiStatic.forgot_link(forgot_key=forgot_key)
send_email_completed = send_email(
subject=f"Dear {found_user.user_tag}, your forgot password link has been sent.",
receivers=[str(found_user.email)],
html=change_your_password_template(
user_name=found_user.user_tag, forgot_link=forgot_link
),
)
if not send_email_completed:
raise HTTPException(
status_code=400, detail="Email can not be sent. Try again later"
)
found_user.password_token = forgot_key
found_user.password_token_is_valid = str(system_arrow.shift(days=1))
found_user.save()
return ResponseHandler.success(
"Password is change link is sent to your email or phone",
data={},
)
class AuthenticationResetPasswordEventMethods(MethodToEvent):
event_type = "UPDATE"
__event_keys__ = {
"af9e121e-24bb-44ac-a616-471d5754360e": "authentication_reset_password",
}
@classmethod
def authentication_reset_password(cls, data: Forgot):
from sqlalchemy import or_
found_user = Users.query.filter(
or_(
Users.email == str(data.access_key).lower(),
Users.phone_number == str(data.access_key).replace(" ", ""),
),
).first()
if not found_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Given access key or domain is not matching with the any user record.",
)
reset_password_token = found_user.reset_password_token(found_user=found_user)
send_email_completed = send_email(
subject=f"Dear {found_user.user_tag}, a password reset request has been received.",
receivers=[str(found_user.email)],
html=change_your_password_template(
user_name=found_user.user_tag,
forgot_link=ApiStatic.forgot_link(forgot_key=reset_password_token),
),
)
if not send_email_completed:
raise found_user.raise_http_exception(
status_code=400, message="Email can not be sent. Try again later"
)
return ResponseHandler.success(
"Password change link is sent to your email or phone",
data=found_user.get_dict(),
)
class AuthenticationDownloadAvatarEventMethods(MethodToEvent):
event_type = "LOGIN"
event_description = "Download avatar icon and profile info of user"
event_category = "AUTHENTICATION"
__event_keys__ = {
"c140cd5f-307f-4046-a93e-3ade032a57a7": "authentication_download_avatar",
}
__event_validation__ = {
"c140cd5f-307f-4046-a93e-3ade032a57a7": AuthenticationUserInfoResponse,
}
@classmethod
def authentication_download_avatar(
cls, token_dict: Union[EmployeeTokenObject, OccupantTokenObject]
):
if found_user := Users.filter_one(Users.id == token_dict.user_id).data:
expired_starts = str(
system_arrow.now() - system_arrow.get(str(found_user.expiry_ends))
)
expired_int = (
system_arrow.now() - system_arrow.get(str(found_user.expiry_ends))
).days
user_info = {
"lang": token_dict.lang,
"full_name": found_user.person.full_name,
"avatar": found_user.avatar,
"remember_me": found_user.remember_me,
"expiry_ends": str(found_user.expiry_ends),
"expired_str": expired_starts,
"expired_int": int(expired_int),
}
return ResponseHandler.success(
"Avatar and profile is shared via user credentials",
data=user_info,
)
return ResponseHandler.not_found("Invalid data")
AuthenticationLoginEventMethod = AuthenticationLoginEventMethods(
action=ActionsSchema(endpoint="/authentication/login")
)
AuthenticationSelectEventMethod = AuthenticationSelectEventMethods(
action=ActionsSchema(endpoint="/authentication/select")
)
AuthenticationCheckTokenEventMethod = AuthenticationCheckTokenEventMethods(
action=ActionsSchema(endpoint="/authentication/valid")
)
AuthenticationRefreshEventMethod = AuthenticationRefreshEventMethods(
action=ActionsSchema(endpoint="/authentication/refresh")
)
AuthenticationChangePasswordEventMethod = AuthenticationChangePasswordEventMethods(
action=ActionsSchema(endpoint="/authentication/change_password")
)
AuthenticationCreatePasswordEventMethod = AuthenticationCreatePasswordEventMethods(
action=ActionsSchema(endpoint="/authentication/create_password")
)
AuthenticationDisconnectUserEventMethod = AuthenticationDisconnectUserEventMethods(
action=ActionsSchema(endpoint="/authentication/disconnect")
)
AuthenticationLogoutEventMethod = AuthenticationLogoutEventMethods(
action=ActionsSchema(endpoint="/authentication/logout")
)
AuthenticationRefreshTokenEventMethod = AuthenticationRefreshTokenEventMethods(
action=ActionsSchema(endpoint="/authentication/refresher")
)
AuthenticationForgotPasswordEventMethod = AuthenticationForgotPasswordEventMethods(
action=ActionsSchema(endpoint="/authentication/forgot")
)
AuthenticationDownloadAvatarEventMethod = AuthenticationDownloadAvatarEventMethods(
action=ActionsSchema(endpoint="/authentication/avatar")
)
AuthenticationResetPasswordEventMethod = AuthenticationResetPasswordEventMethods(
action=ActionsSchema(endpoint="/authentication/reset_password")
)

View File

@ -1,5 +0,0 @@
from api_events.events.abstract_class import MethodToEvent, ActionsSchema
from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject
class RulesEvents(MethodToEvent): ...

View File

@ -80,7 +80,7 @@ class BuildAreaCreateEventMethods(MethodToEvent):
selected_build = None selected_build = None
if isinstance(token_dict, OccupantTokenObject): if isinstance(token_dict, OccupantTokenObject):
if not token_dict.selected_occupant.build_uuid == data.build_uu_id: if not token_dict.selected_occupant.build_uuid == data.build_uu_id:
BuildArea.raise_http_exception( raise BuildArea.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"Occupant can not create build area for {data.build_uu_id}", message=f"Occupant can not create build area for {data.build_uu_id}",
@ -96,7 +96,7 @@ class BuildAreaCreateEventMethods(MethodToEvent):
employee_id=token_dict.selected_company.employee_id employee_id=token_dict.selected_company.employee_id
).all() ).all()
if not str(data.build_uu_id) in [str(build.uu_id) for build in build_ids]: if not str(data.build_uu_id) in [str(build.uu_id) for build in build_ids]:
BuildArea.raise_http_exception( raise BuildArea.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"Employee can not create build area for {data.build_uu_id}", message=f"Employee can not create build area for {data.build_uu_id}",

View File

@ -47,7 +47,7 @@ class BuildSitesListEventMethods(MethodToEvent):
) )
employees_build_list = [build.address_id for build in employees_build.all()] employees_build_list = [build.address_id for build in employees_build.all()]
if not employees_build_list: if not employees_build_list:
BuildSites.raise_http_exception( raise BuildSites.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="NOT_FOUND", error_case="NOT_FOUND",
message="Employee has no build sites registered", message="Employee has no build sites registered",
@ -86,7 +86,7 @@ class BuildSitesCreateEventMethods(MethodToEvent):
): ):
if isinstance(token_dict, OccupantTokenObject): if isinstance(token_dict, OccupantTokenObject):
if not token_dict.selected_occupant.build_uuid == data.build_uu_id: if not token_dict.selected_occupant.build_uuid == data.build_uu_id:
BuildSites.raise_http_exception( raise BuildSites.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"Occupant can not create build sites for {data.build_uu_id}", message=f"Occupant can not create build sites for {data.build_uu_id}",
@ -99,7 +99,7 @@ class BuildSitesCreateEventMethods(MethodToEvent):
employee_id=token_dict.selected_company.employee_id employee_id=token_dict.selected_company.employee_id
).all() ).all()
if not str(data.build_uu_id) in [str(build.uu_id) for build in build_ids]: if not str(data.build_uu_id) in [str(build.uu_id) for build in build_ids]:
BuildSites.raise_http_exception( raise BuildSites.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"Employee can not create build sites for {data.build_uu_id}", message=f"Employee can not create build sites for {data.build_uu_id}",

View File

@ -47,7 +47,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent):
Build.id == token_dict.selected_occupant.build_id, Build.id == token_dict.selected_occupant.build_id,
).data ).data
if not occupants_build_id: if not occupants_build_id:
Build.raise_http_exception( raise Build.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"Occupant has no build registered in the system. Contact with your company.", message=f"Occupant has no build registered in the system. Contact with your company.",
@ -57,7 +57,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent):
BuildParts.build_id.in_(occupants_build_id.id), BuildParts.build_id.in_(occupants_build_id.id),
).data ).data
if not occupants_build_parts: if not occupants_build_parts:
Build.raise_http_exception( raise Build.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"Occupant has no build parts registered in the system. Contact with your company.", message=f"Occupant has no build parts registered in the system. Contact with your company.",
@ -80,7 +80,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent):
employee_id=token_dict.selected_company.employee_id employee_id=token_dict.selected_company.employee_id
) )
if not build_id_list_query: if not build_id_list_query:
Build.raise_http_exception( raise Build.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"Employee has no build registered in the system. Contact with your supervisor.", message=f"Employee has no build registered in the system. Contact with your supervisor.",
@ -92,7 +92,7 @@ class BuildingLivingSpacesListEventMethods(MethodToEvent):
), ),
).data ).data
if not build_part_id_list_query: if not build_part_id_list_query:
Build.raise_http_exception( raise Build.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"Employee has no build parts registered in the system. Contact with your supervisor.", message=f"Employee has no build parts registered in the system. Contact with your supervisor.",
@ -142,7 +142,7 @@ class BuildingLivingSpacesCreateEventMethods(MethodToEvent):
BuildParts.build_id.in_([build.id for build in build_id_list_query.all()]), BuildParts.build_id.in_([build.id for build in build_id_list_query.all()]),
).data ).data
if not build_part: if not build_part:
BuildLivingSpace.raise_http_exception( raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"{data.build_parts_uu_id} - Build Part is not found in database. Check build part uu_id", message=f"{data.build_parts_uu_id} - Build Part is not found in database. Check build part uu_id",
@ -154,7 +154,7 @@ class BuildingLivingSpacesCreateEventMethods(MethodToEvent):
People.uu_id == data.person_uu_id, People.uu_id == data.person_uu_id,
).data ).data
if not life_person: if not life_person:
BuildLivingSpace.raise_http_exception( raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"{data.person_uu_id} - Living Person is not found in database.", message=f"{data.person_uu_id} - Living Person is not found in database.",
@ -164,7 +164,7 @@ class BuildingLivingSpacesCreateEventMethods(MethodToEvent):
) )
occupant_type = OccupantTypes.filter_by_one(uu_id=data.occupant_type_uu_id).data occupant_type = OccupantTypes.filter_by_one(uu_id=data.occupant_type_uu_id).data
if not occupant_type: if not occupant_type:
BuildLivingSpace.raise_http_exception( raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_404_NOT_FOUND", status_code="HTTP_404_NOT_FOUND",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"{data.occupant_type_uu_id} - Occupant Type is not found in database. Check occupant type uu_id", message=f"{data.occupant_type_uu_id} - Occupant Type is not found in database. Check occupant type uu_id",
@ -252,7 +252,7 @@ class BuildingLivingSpacesUpdateEventMethods(MethodToEvent):
), ),
).data ).data
if not build_part: if not build_part:
BuildLivingSpace.raise_http_exception( raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"{data.life_person_uu_id} - Living Person is not found in database.", message=f"{data.life_person_uu_id} - Living Person is not found in database.",
@ -264,7 +264,7 @@ class BuildingLivingSpacesUpdateEventMethods(MethodToEvent):
People.uu_id == data.life_person_uu_id or "" People.uu_id == data.life_person_uu_id or ""
).data ).data
if not life_person: if not life_person:
BuildLivingSpace.raise_http_exception( raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message=f"{data.life_person_uu_id} - Living Person is not found in database.", message=f"{data.life_person_uu_id} - Living Person is not found in database.",
@ -290,7 +290,7 @@ class BuildingLivingSpacesUpdateEventMethods(MethodToEvent):
if data_dict["is_tenant_live"]: if data_dict["is_tenant_live"]:
owner_person = getattr(last_living_space, "owner_person_id", None) owner_person = getattr(last_living_space, "owner_person_id", None)
if not owner_person: if not owner_person:
BuildLivingSpace.raise_http_exception( raise BuildLivingSpace.raise_http_exception(
status_code="HTTP_403_FORBIDDEN", status_code="HTTP_403_FORBIDDEN",
error_case="UNAUTHORIZED", error_case="UNAUTHORIZED",
message="Owner person of build part is not defined. Please register owner of part first.", message="Owner person of build part is not defined. Please register owner of part first.",

View File

@ -6,6 +6,7 @@ from api_objects.auth.token_objects import (
OccupantToken, OccupantToken,
ApplicationToken, ApplicationToken,
) )
from api_objects.errors.errorHandlers import HTTPExceptionEvyos
__all__ = [ __all__ = [
"OccupantTokenObject", "OccupantTokenObject",
@ -14,4 +15,5 @@ __all__ = [
"CompanyToken", "CompanyToken",
"OccupantToken", "OccupantToken",
"ApplicationToken", "ApplicationToken",
"HTTPExceptionEvyos",
] ]

View File

@ -23,7 +23,7 @@ class ApplicationToken(BaseModel):
domain: Optional[str] = "app.evyos.com.tr" domain: Optional[str] = "app.evyos.com.tr"
lang: Optional[str] = "TR" lang: Optional[str] = "TR"
timezone: Optional[str] = "Europe/Istanbul" timezone: Optional[str] = "GMT+3"
user_type: int = UserType.occupant.value user_type: int = UserType.occupant.value
credentials: dict = None credentials: dict = None

View File

@ -1,74 +1,6 @@
from .errors_dictionary import ErrorMessages
class AlchemyError: class HTTPExceptionError:
ERRORS_DICT = {
"100": "HTTP_100_CONTINUE",
"101": "HTTP_101_SWITCHING_PROTOCOLS",
"102": "HTTP_102_PROCESSING",
"103": "HTTP_103_EARLY_HINTS",
"200": "HTTP_200_OK",
"201": "HTTP_201_CREATED",
"202": "HTTP_202_ACCEPTED",
"203": "HTTP_203_NON_AUTHORITATIVE_INFORMATION",
"204": "HTTP_204_NO_CONTENT",
"205": "HTTP_205_RESET_CONTENT",
"206": "HTTP_206_PARTIAL_CONTENT",
"207": "HTTP_207_MULTI_STATUS",
"208": "HTTP_208_ALREADY_REPORTED",
"226": "HTTP_226_IM_USED",
"300": "HTTP_300_MULTIPLE_CHOICES",
"301": "HTTP_301_MOVED_PERMANENTLY",
"302": "HTTP_302_FOUND",
"303": "HTTP_303_SEE_OTHER",
"304": "HTTP_304_NOT_MODIFIED",
"305": "HTTP_305_USE_PROXY",
"306": "HTTP_306_RESERVED",
"307": "HTTP_307_TEMPORARY_REDIRECT",
"308": "HTTP_308_PERMANENT_REDIRECT",
"400": "HTTP_400_BAD_REQUEST",
"401": "HTTP_401_UNAUTHORIZED",
"402": "HTTP_402_PAYMENT_REQUIRED",
"403": "HTTP_403_FORBIDDEN",
"404": "HTTP_404_NOT_FOUND",
"405": "HTTP_405_METHOD_NOT_ALLOWED",
"406": "HTTP_406_NOT_ACCEPTABLE",
"407": "HTTP_407_PROXY_AUTHENTICATION_REQUIRED",
"408": "HTTP_408_REQUEST_TIMEOUT",
"409": "HTTP_409_CONFLICT",
"410": "HTTP_410_GONE",
"411": "HTTP_411_LENGTH_REQUIRED",
"412": "HTTP_412_PRECONDITION_FAILED",
"413": "HTTP_413_REQUEST_ENTITY_TOO_LARGE",
"414": "HTTP_414_REQUEST_URI_TOO_LONG",
"415": "HTTP_415_UNSUPPORTED_MEDIA_TYPE",
"416": "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE",
"417": "HTTP_417_EXPECTATION_FAILED",
"418": "HTTP_418_IM_A_TEAPOT",
"421": "HTTP_421_MISDIRECTED_REQUEST",
"422": "HTTP_422_UNPROCESSABLE_ENTITY",
"423": "HTTP_423_LOCKED",
"424": "HTTP_424_FAILED_DEPENDENCY",
"426": "HTTP_426_UPGRADE_REQUIRED",
"428": "HTTP_428_PRECONDITION_REQUIRED",
"429": "HTTP_429_TOO_MANY_REQUESTS",
"431": "HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE",
"451": "HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS",
"500": "HTTP_500_INTERNAL_SERVER_ERROR",
}
ERRORS_KEYS = {
"delete": "DeletedRecord",
"update": "UpdatedRecord",
"create": "CreatedRecord",
"list": "ListedRecords",
"not_found": "RecordNotFound",
"already_exist": "AlreadyExists",
"not_deleted": "RecordNotDeleted",
"not_updated": "RecordNotUpdated",
"not_created": "RecordNotCreated",
"not_listed": "RecordsNotListed",
"not_confirm": "IsNotConfirmed",
}
def __init__(self, lang): def __init__(self, lang):
self.lang = lang self.lang = lang

View File

@ -0,0 +1,60 @@
from typing import Any
# class HTTPExceptionInstance:
# def __init__(self, statuses, exceptions, exceptions_dict, errors_dict, response_model, error_language_dict):
# self.EXCEPTIONS = exceptions # from fastapi.exceptions import HTTPException
# self.STATUSES = statuses # from fastapi import status
# self.EXCEPTION_DICTS: dict = exceptions_dict
# self.ERRORS_DICT: dict = errors_dict
# self.ERRORS_LANG: dict = error_language_dict
# self.RESPONSE_MODEL: Any = response_model
class HTTPExceptionEvyos(Exception):
def __init__(self, error_code: str, lang: str):
self.error_code = error_code
self.lang = lang
class HTTPExceptionEvyosHandler:
def __init__(self, statuses, exceptions, exceptions_dict, errors_dict, response_model, error_language_dict):
self.EXCEPTIONS = exceptions # from fastapi.exceptions import HTTPException
self.STATUSES = statuses # from fastapi import status
self.EXCEPTION_DICTS: dict = exceptions_dict
self.ERRORS_DICT: dict = errors_dict
self.ERRORS_LANG: dict = error_language_dict
self.RESPONSE_MODEL: Any = response_model
def retrieve_error_status_code(self, exc: HTTPExceptionEvyos):
grab_status = self.ERRORS_DICT.get(str(exc.error_code).upper(), "")
grab_status_code = self.EXCEPTION_DICTS.get(str(grab_status).upper(), "500")
return getattr(self.STATUSES, str(grab_status_code), getattr(self.STATUSES, "HTTP_500_INTERNAL_SERVER_ERROR"))
def retrieve_error_message(self, exc: HTTPExceptionEvyos):
message_by_lang = self.ERRORS_LANG.get(str(exc.lang).lower(), {})
message_by_code = message_by_lang.get(str(exc.error_code).upper(), "Unknown error")
return message_by_code
def handle_exception(self, request, exc: HTTPExceptionEvyos):
headers = getattr(request, "headers", {})
status_code = self.retrieve_error_status_code(exc)
error_message = self.retrieve_error_message(exc)
return self.RESPONSE_MODEL(
status_code=int(status_code),
content={"message": error_message, "lang": exc.lang},
)
class HTTPExceptionAnyHandler:
def __init__(self, response_model):
self.RESPONSE_MODEL: Any = response_model
def any_exception_handler(self, request, exc: Exception):
return self.RESPONSE_MODEL(
status_code=200,
content={"message": str(exc), "lang": None, "status_code": 417},
)

View File

@ -0,0 +1,17 @@
from .baseErrorCluster import (
BASE_ERRORS,
BASE_ERROR_LANGUAGE,
EXCEPTION_DICTS,
)
ERRORS_LANG = {
"tr": {
**BASE_ERROR_LANGUAGE["tr"],
},
"en": {
**BASE_ERROR_LANGUAGE["en"],
}
}
ERRORS_DICT = {
**BASE_ERRORS,
}

View File

@ -0,0 +1,104 @@
BASE_ERRORS = {
"NOT_CREATED": 405,
"NOT_DELETED": 405,
"NOT_UPDATED": 405,
"NOT_LISTED": 404,
"NOT_FOUND": 404,
"ALREADY_EXISTS": 400,
"IS_NOT_CONFIRMED": 405,
"NOT_AUTHORIZED": 401,
"NOT_VALID": 406,
"NOT_ACCEPTABLE": 406,
"INVALID_DATA": 422,
"UNKNOWN_ERROR": 502,
}
BASE_ERROR_LANGUAGE = {
"tr": {
"NOT_CREATED": "Kayıt oluşturulamadı.",
"NOT_DELETED": "Kayıt silinemedi.",
"NOT_UPDATED": "Kayıt güncellenemedi.",
"NOT_LISTED": "Kayıt listelenemedi.",
"NOT_FOUND": "Kayıt bulunamadı.",
"ALREADY_EXISTS": "Kayıt zaten mevcut.",
"IS_NOT_CONFIRMED": "Kayıt onaylanmadı.",
"NOT_AUTHORIZED": "Yetkisiz kullanıcı.",
"NOT_VALID": "Gecersiz veri.",
"NOT_ACCEPTABLE": "Gecersiz veri.",
"INVALID_DATA": "Gecersiz veri.",
"UNKNOWN_ERROR": "Bilinmeyen bir hata oluştu.",
},
"en": {
"NOT_CREATED": "Not Created.",
"NOT_DELETED": "Not Deleted.",
"NOT_UPDATED": "Not Updated.",
"NOT_LISTED": "Not Listed.",
"NOT_FOUND": "Not Found.",
"ALREADY_EXISTS": "Already Exists.",
"IS_NOT_CONFIRMED": "Not Confirmed.",
"NOT_AUTHORIZED": "Not Authorized.",
"NOT_VALID": "Not Valid.",
"NOT_ACCEPTABLE": "Not Acceptable.",
"INVALID_DATA": "Invalid Data.",
"UNKNOWN_ERROR": "Unknown Error occured.",
},
}
from fastapi import status
EXCEPTION_DICTS = {
"100": "HTTP_100_CONTINUE",
"101": "HTTP_101_SWITCHING_PROTOCOLS",
"102": "HTTP_102_PROCESSING",
"103": "HTTP_103_EARLY_HINTS",
"200": "HTTP_200_OK",
"201": "HTTP_201_CREATED",
"202": "HTTP_202_ACCEPTED",
"203": "HTTP_203_NON_AUTHORITATIVE_INFORMATION",
"204": "HTTP_204_NO_CONTENT",
"205": "HTTP_205_RESET_CONTENT",
"206": "HTTP_206_PARTIAL_CONTENT",
"207": "HTTP_207_MULTI_STATUS",
"208": "HTTP_208_ALREADY_REPORTED",
"226": "HTTP_226_IM_USED",
"300": "HTTP_300_MULTIPLE_CHOICES",
"301": "HTTP_301_MOVED_PERMANENTLY",
"302": "HTTP_302_FOUND",
"303": "HTTP_303_SEE_OTHER",
"304": "HTTP_304_NOT_MODIFIED",
"305": "HTTP_305_USE_PROXY",
"306": "HTTP_306_RESERVED",
"307": "HTTP_307_TEMPORARY_REDIRECT",
"308": "HTTP_308_PERMANENT_REDIRECT",
"400": "HTTP_400_BAD_REQUEST",
"401": "HTTP_401_UNAUTHORIZED",
"402": "HTTP_402_PAYMENT_REQUIRED",
"403": "HTTP_403_FORBIDDEN",
"404": "HTTP_404_NOT_FOUND",
"405": "HTTP_405_METHOD_NOT_ALLOWED",
"406": "HTTP_406_NOT_ACCEPTABLE",
"407": "HTTP_407_PROXY_AUTHENTICATION_REQUIRED",
"408": "HTTP_408_REQUEST_TIMEOUT",
"409": "HTTP_409_CONFLICT",
"410": "HTTP_410_GONE",
"411": "HTTP_411_LENGTH_REQUIRED",
"412": "HTTP_412_PRECONDITION_FAILED",
"413": "HTTP_413_REQUEST_ENTITY_TOO_LARGE",
"414": "HTTP_414_REQUEST_URI_TOO_LONG",
"415": "HTTP_415_UNSUPPORTED_MEDIA_TYPE",
"416": "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE",
"417": "HTTP_417_EXPECTATION_FAILED",
"418": "HTTP_418_IM_A_TEAPOT",
"421": "HTTP_421_MISDIRECTED_REQUEST",
"422": "HTTP_422_UNPROCESSABLE_ENTITY",
"423": "HTTP_423_LOCKED",
"424": "HTTP_424_FAILED_DEPENDENCY",
"426": "HTTP_426_UPGRADE_REQUIRED",
"428": "HTTP_428_PRECONDITION_REQUIRED",
"429": "HTTP_429_TOO_MANY_REQUESTS",
"431": "HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE",
"451": "HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS",
"500": "HTTP_500_INTERNAL_SERVER_ERROR",
"502": "HTTP_502_BAD_GATEWAY",
}

View File

@ -1,49 +0,0 @@
from json import loads
class ErrorMessages:
__messages__ = {}
@classmethod
def get_message(cls, message_key, lang):
return cls.__messages__[lang][message_key]
class ErrorHandlers:
def __init__(self, requests, exceptions, response_model, status):
self.requests = requests # from fastapi.requests import Request
self.exceptions = exceptions # from fastapi.exceptions import HTTPException
self.response_model = (
response_model # from fastapi.responses import JSONResponse
)
self.status = status # from fastapi import status
@classmethod
def create(cls, requests, exceptions, response_model, status):
return cls(requests, exceptions, response_model, status)
def exception_handler_http(self, request, exc):
exc_detail = getattr(exc, "detail", None)
try:
detail = loads(str(exc_detail))
return self.response_model(
status_code=exc.status_code,
content={
"Data": detail.get("data", {}),
"Error": detail.get("error_case", "UNKNOWN"),
"Message": detail.get(
"message", "An error occurred while processing the request"
),
},
)
except Exception as e:
return self.response_model(
status_code=exc.status_code,
content={"Error": str(exc_detail), "Message": f"{str(e)}", "Data": {}},
)
def exception_handler_exception(self, request, exc):
return self.response_model(
status_code=self.status.HTTP_417_EXPECTATION_FAILED,
content={"message": exc.__str__()},
)

View File

@ -1,21 +1,22 @@
import hashlib import hashlib
import uuid import uuid
import requests import requests
import secrets
from datetime import timedelta from datetime import timedelta
from fastapi.exceptions import HTTPException
from api_services.redis.functions import RedisActions
from databases.no_sql_models.validations import ( from databases.no_sql_models.validations import (
PasswordHistoryViaUser, PasswordHistoryViaUser,
AccessHistoryViaUser, AccessHistoryViaUser,
) )
from api_library.date_time_actions.date_functions import system_arrow, client_arrow from api_library.date_time_actions.date_functions import system_arrow
from api_configs import ApiStatic, Auth from api_configs import ApiStatic, Auth
class PasswordModule: class PasswordModule:
@staticmethod @staticmethod
def generate_token(length=32): def generate_token(length=32):
return uuid.uuid4().__str__()[:length] return secrets.token_urlsafe(length)
@staticmethod @staticmethod
def create_hashed_password(domain: str, id_: str, password: str): def create_hashed_password(domain: str, id_: str, password: str):
@ -25,6 +26,10 @@ class PasswordModule:
def check_password(cls, domain, id_, password, password_hashed): def check_password(cls, domain, id_, password, password_hashed):
return cls.create_hashed_password(domain, id_, password) == password_hashed return cls.create_hashed_password(domain, id_, password) == password_hashed
@classmethod
def generate_access_token(cls):
return secrets.token_urlsafe(Auth.ACCESS_TOKEN_LENGTH)
class AuthModule(PasswordModule): class AuthModule(PasswordModule):
@ -33,6 +38,7 @@ class AuthModule(PasswordModule):
from databases import Users from databases import Users
from sqlalchemy import or_ from sqlalchemy import or_
from fastapi import status from fastapi import status
from fastapi.exceptions import HTTPException
found_user: Users = Users.query.filter( found_user: Users = Users.query.filter(
or_( or_(
@ -56,9 +62,6 @@ class AuthModule(PasswordModule):
) )
return found_user return found_user
def generate_access_token(self):
return self.generate_token(Auth.ACCESS_TOKEN_LENGTH)
def remove_refresher_token(self, domain, disconnect: bool = False): def remove_refresher_token(self, domain, disconnect: bool = False):
from databases import UsersTokens from databases import UsersTokens
@ -76,6 +79,8 @@ class AuthModule(PasswordModule):
UsersTokens.save() UsersTokens.save()
def check_password_is_different(self, password): def check_password_is_different(self, password):
from fastapi.exceptions import HTTPException
main_domain = self.get_main_domain_and_other_domains(get_main_domain=True) main_domain = self.get_main_domain_and_other_domains(get_main_domain=True)
if self.hash_password == self.create_hashed_password( if self.hash_password == self.create_hashed_password(
domain=main_domain, id_=self.uu_id, password=password domain=main_domain, id_=self.uu_id, password=password
@ -88,9 +93,9 @@ class AuthModule(PasswordModule):
@staticmethod @staticmethod
def create_password(found_user, password, password_token=None): def create_password(found_user, password, password_token=None):
from databases import MongoQueryIdentity, Users from databases import MongoQueryIdentity, Users
from fastapi.exceptions import HTTPException
found_user: Users = found_user found_user: Users = found_user
if found_user.password_token: if found_user.password_token:
replace_day = 0 replace_day = 0
try: try:
@ -145,9 +150,7 @@ class AuthModule(PasswordModule):
return found_user.password_token return found_user.password_token
def generate_refresher_token(self, domain: str, remember_me=False): def generate_refresher_token(self, domain: str, remember_me=False):
from databases import ( from databases import UsersTokens
UsersTokens,
)
if remember_me: if remember_me:
refresh_token = self.generate_token(Auth.REFRESHER_TOKEN_LENGTH) refresh_token = self.generate_token(Auth.REFRESHER_TOKEN_LENGTH)
@ -183,6 +186,7 @@ class AuthModule(PasswordModule):
class UserLoginModule(AuthModule): class UserLoginModule(AuthModule):
@classmethod @classmethod
def set_login_details_to_mongo_database( def set_login_details_to_mongo_database(
cls, found_user, headers_request, access_token, record_id cls, found_user, headers_request, access_token, record_id
@ -212,8 +216,8 @@ class UserLoginModule(AuthModule):
"address": address_package, "address": address_package,
"user_id": found_user.id, "user_id": found_user.id,
} }
already_exits = mongo_db.mongo_engine.filter_by(filter_query) or None already_exits = mongo_db.mongo_engine.filter_by(filter_query).data
no_address_validates = mongo_db.mongo_engine.get_all()[0] == 0 no_address_validates = mongo_db.mongo_engine.get_all().data
access_via_user = query_engine.update_access_history_via_user( access_via_user = query_engine.update_access_history_via_user(
AccessHistoryViaUser( AccessHistoryViaUser(
**{ **{
@ -241,7 +245,6 @@ class UserLoginModule(AuthModule):
} }
}, },
) )
print("update_mongo", update_mongo)
else: else:
insert_mongo = mongo_db.mongo_engine.insert( insert_mongo = mongo_db.mongo_engine.insert(
payload={ payload={
@ -257,14 +260,14 @@ class UserLoginModule(AuthModule):
"is_first": True if no_address_validates else False, "is_first": True if no_address_validates else False,
} }
) )
print("insert_mongo", insert_mongo)
@classmethod @classmethod
def login_user_with_credentials(cls, data, request): def login_user_with_credentials(cls, data, request):
from databases.sql_models.identity.identity import Users from databases.sql_models.identity.identity import Users
from ApiServices.api_handlers.auth_actions.auth import AuthActions from ApiServices.api_handlers.auth_actions.auth import AuthActions
from fastapi.exceptions import HTTPException
found_user = Users.check_user_exits( found_user: Users = Users.check_user_exits(
access_key=data.access_key, domain=data.domain access_key=data.access_key, domain=data.domain
) )
if not found_user: if not found_user:
@ -280,19 +283,20 @@ class UserLoginModule(AuthModule):
) )
if found_user.check_password( if found_user.check_password(
domain=data.domain, domain=data.domain,
id_=found_user.uu_id, id_=found_user.uu_id,
password_hashed=found_user.hash_password, password_hashed=found_user.hash_password,
password=data.password, password=data.password,
): ):
access_object_to_redis = AuthActions.save_access_token_to_redis( access_object_to_redis = AuthActions.save_access_token_to_redis(
request=request, request=request,
found_user=found_user, found_user=found_user,
domain=data.domain, domain=data.domain,
# remember_me=data.remember_me, # remember_me=data.remember_me,
) )
print("access_object_to_redis", access_object_to_redis)
access_token = access_object_to_redis.get("access_token") access_token = access_object_to_redis.get("access_token")
access_object_to_redis["user"] = found_user
headers_request = dict(request.headers) headers_request = dict(request.headers)
headers_request["evyos-user-agent"] = headers_request.get("user-agent") headers_request["evyos-user-agent"] = headers_request.get("user-agent")
headers_request["evyos-platform"] = headers_request.get("user-agent") headers_request["evyos-platform"] = headers_request.get("user-agent")

View File

@ -17,7 +17,7 @@ from sqlalchemy import (
Numeric, Numeric,
Integer, Integer,
) )
from sqlalchemy.orm import mapped_column, Mapped, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship
from api_validations.validations_request import ( from api_validations.validations_request import (
InsertDecisionBook, InsertDecisionBook,
@ -26,6 +26,19 @@ from api_validations.validations_request import (
InsertBuildDecisionBookProjects, InsertBuildDecisionBookProjects,
) )
from databases.sql_models.core_mixin import CrudCollection from databases.sql_models.core_mixin import CrudCollection
from databases.language_models.building.decision_book import (
BuildDecisionBookLanguageModel,
BuildDecisionBookInvitationsLanguageModel,
BuildDecisionBookPersonLanguageModel,
BuildDecisionBookPersonOccupantsLanguageModel,
BuildDecisionBookItemsLanguageModel,
BuildDecisionBookItemsUnapprovedLanguageModel,
BuildDecisionBookPaymentsLanguageModel,
BuildDecisionBookLegalLanguageModel,
BuildDecisionBookProjectsLanguageModel,
BuildDecisionBookProjectPersonLanguageModel,
BuildDecisionBookProjectItemsLanguageModel,
)
class BuildDecisionBook(CrudCollection): class BuildDecisionBook(CrudCollection):
@ -44,6 +57,7 @@ class BuildDecisionBook(CrudCollection):
__tablename__ = "build_decision_book" __tablename__ = "build_decision_book"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookLanguageModel
decision_book_pdf_path: Mapped[str] = mapped_column( decision_book_pdf_path: Mapped[str] = mapped_column(
String, server_default="", nullable=True String, server_default="", nullable=True
@ -242,6 +256,7 @@ class BuildDecisionBookInvitations(CrudCollection):
__tablename__ = "build_decision_book_invitations" __tablename__ = "build_decision_book_invitations"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookInvitationsLanguageModel
build_id: Mapped[int] = mapped_column(Integer, nullable=False) build_id: Mapped[int] = mapped_column(Integer, nullable=False)
build_uu_id: Mapped[str] = mapped_column( build_uu_id: Mapped[str] = mapped_column(
@ -341,6 +356,7 @@ class BuildDecisionBookPerson(CrudCollection):
__tablename__ = "build_decision_book_person" __tablename__ = "build_decision_book_person"
__exclude__fields__ = [] __exclude__fields__ = []
__enum_list__ = [("management_typecode", "BuildManagementType", "bm")] __enum_list__ = [("management_typecode", "BuildManagementType", "bm")]
__language_model__ = BuildDecisionBookPersonLanguageModel
dues_percent_discount: Mapped[int] = mapped_column(SmallInteger, server_default="0") dues_percent_discount: Mapped[int] = mapped_column(SmallInteger, server_default="0")
dues_fix_discount: Mapped[float] = mapped_column(Numeric(10, 2), server_default="0") dues_fix_discount: Mapped[float] = mapped_column(Numeric(10, 2), server_default="0")
@ -517,6 +533,7 @@ class BuildDecisionBookPersonOccupants(CrudCollection):
__tablename__ = "build_decision_book_person_occupants" __tablename__ = "build_decision_book_person_occupants"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookPersonOccupantsLanguageModel
build_decision_book_person_id: Mapped[int] = mapped_column( build_decision_book_person_id: Mapped[int] = mapped_column(
ForeignKey("build_decision_book_person.id"), nullable=False ForeignKey("build_decision_book_person.id"), nullable=False
@ -559,6 +576,7 @@ class BuildDecisionBookItems(CrudCollection):
__tablename__ = "build_decision_book_items" __tablename__ = "build_decision_book_items"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookItemsLanguageModel
item_order: Mapped[int] = mapped_column( item_order: Mapped[int] = mapped_column(
SmallInteger, nullable=False, comment="Order Number of Item" SmallInteger, nullable=False, comment="Order Number of Item"
@ -799,6 +817,7 @@ class BuildDecisionBookItemsUnapproved(CrudCollection):
__tablename__ = "build_decision_book_items_unapproved" __tablename__ = "build_decision_book_items_unapproved"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookItemsUnapprovedLanguageModel
item_objection: Mapped[str] = mapped_column( item_objection: Mapped[str] = mapped_column(
Text, nullable=False, comment="Objection Content" Text, nullable=False, comment="Objection Content"
@ -841,6 +860,7 @@ class BuildDecisionBookPayments(CrudCollection):
__tablename__ = "build_decision_book_payments" __tablename__ = "build_decision_book_payments"
__exclude__fields__ = [] __exclude__fields__ = []
__enum_list__ = [("receive_debit", "DebitTypes", "D")] __enum_list__ = [("receive_debit", "DebitTypes", "D")]
__language_model__ = BuildDecisionBookPaymentsLanguageModel
payment_plan_time_periods: Mapped[str] = mapped_column( payment_plan_time_periods: Mapped[str] = mapped_column(
String(10), nullable=False, comment="Payment Plan Time Periods" String(10), nullable=False, comment="Payment Plan Time Periods"
@ -951,6 +971,7 @@ class BuildDecisionBookLegal(CrudCollection):
__tablename__ = "build_decision_book_legal" __tablename__ = "build_decision_book_legal"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookLegalLanguageModel
period_start_date: Mapped[TIMESTAMP] = mapped_column( period_start_date: Mapped[TIMESTAMP] = mapped_column(
TIMESTAMP(timezone=True), nullable=False, comment="Start Date of Legal Period" TIMESTAMP(timezone=True), nullable=False, comment="Start Date of Legal Period"
@ -1027,6 +1048,7 @@ class BuildDecisionBookProjects(CrudCollection):
__tablename__ = "build_decision_book_projects" __tablename__ = "build_decision_book_projects"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookProjectsLanguageModel
project_no: Mapped[str] = mapped_column( project_no: Mapped[str] = mapped_column(
String(12), nullable=True, comment="Project Number of Decision Book" String(12), nullable=True, comment="Project Number of Decision Book"
@ -1194,6 +1216,7 @@ class BuildDecisionBookProjectPerson(CrudCollection):
__tablename__ = "build_decision_book_project_person" __tablename__ = "build_decision_book_project_person"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookProjectPersonLanguageModel
# __enum_list__ = [("management_typecode", "ProjectTeamTypes", "PTT-EMP")] # __enum_list__ = [("management_typecode", "ProjectTeamTypes", "PTT-EMP")]
dues_percent_discount: Mapped[int] = mapped_column(SmallInteger, server_default="0") dues_percent_discount: Mapped[int] = mapped_column(SmallInteger, server_default="0")
@ -1226,6 +1249,7 @@ class BuildDecisionBookProjectItems(CrudCollection):
__tablename__ = "build_decision_book_project_items" __tablename__ = "build_decision_book_project_items"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = BuildDecisionBookProjectItemsLanguageModel
item_header: Mapped[str] = mapped_column( item_header: Mapped[str] = mapped_column(
String, nullable=False, comment="Item Header" String, nullable=False, comment="Item Header"

View File

@ -5,6 +5,13 @@ from sqlalchemy import (
Numeric, Numeric,
) )
from sqlalchemy.orm import mapped_column, Mapped from sqlalchemy.orm import mapped_column, Mapped
from databases.language_models.company.employee import (
StaffLanguageModel,
EmployeesLanguageModel,
EmployeeHistoryLanguageModel,
EmployeesSalariesLanguageModel,
)
from databases.sql_models.core_mixin import CrudCollection from databases.sql_models.core_mixin import CrudCollection
from api_validations.validations_request import InsertCompanyEmployees from api_validations.validations_request import InsertCompanyEmployees
@ -14,6 +21,7 @@ class Staff(CrudCollection):
__tablename__ = "staff" __tablename__ = "staff"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = StaffLanguageModel
staff_description: Mapped[str] = mapped_column( staff_description: Mapped[str] = mapped_column(
String, server_default="", comment="Staff Description" String, server_default="", comment="Staff Description"
@ -61,6 +69,7 @@ class Employees(CrudCollection):
__tablename__ = "employees" __tablename__ = "employees"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = EmployeesLanguageModel
staff_id: Mapped[int] = mapped_column(ForeignKey("staff.id")) staff_id: Mapped[int] = mapped_column(ForeignKey("staff.id"))
staff_uu_id: Mapped[str] = mapped_column( staff_uu_id: Mapped[str] = mapped_column(
@ -81,6 +90,7 @@ class EmployeeHistory(CrudCollection):
__tablename__ = "employee_history" __tablename__ = "employee_history"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = EmployeeHistoryLanguageModel
staff_id: Mapped[int] = mapped_column( staff_id: Mapped[int] = mapped_column(
ForeignKey("staff.id"), nullable=False, comment="Staff ID" ForeignKey("staff.id"), nullable=False, comment="Staff ID"
@ -105,6 +115,7 @@ class EmployeesSalaries(CrudCollection):
__tablename__ = "employee_salaries" __tablename__ = "employee_salaries"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = EmployeesSalariesLanguageModel
gross_salary: Mapped[float] = mapped_column( gross_salary: Mapped[float] = mapped_column(
Numeric(20, 6), nullable=False, comment="Gross Salary" Numeric(20, 6), nullable=False, comment="Gross Salary"

View File

@ -21,7 +21,7 @@ from sqlalchemy_mixins.serialize import SerializeMixin
from sqlalchemy_mixins.repr import ReprMixin from sqlalchemy_mixins.repr import ReprMixin
from sqlalchemy_mixins.smartquery import SmartQueryMixin from sqlalchemy_mixins.smartquery import SmartQueryMixin
from api_library import DateTimeLocal, client_arrow, system_arrow from api_library import DateTimeLocal, system_arrow
from databases.sql_models.sql_operations import FilterAttributes from databases.sql_models.sql_operations import FilterAttributes
from databases.sql_models.postgres_database import Base from databases.sql_models.postgres_database import Base
@ -138,14 +138,14 @@ class CrudMixin(Base, SmartQueryMixin, SessionMixin, FilterAttributes):
return True, int(val) return True, int(val)
elif key_ == Mapped[TIMESTAMP]: elif key_ == Mapped[TIMESTAMP]:
return True, str( return True, str(
cls.client_arrow.get(str(val)).format("DD-MM-YYYY HH:mm:ss") cls.client_arrow.get(str(val)).format("DD-MM-YYYY HH:mm:ss +0")
) )
elif key_ == Mapped[str]: elif key_ == Mapped[str]:
return True, str(val) return True, str(val)
else: else:
if isinstance(val, datetime.datetime): if isinstance(val, datetime.datetime):
return True, str( return True, str(
cls.client_arrow.get(str(val)).format("DD-MM-YYYY HH:mm:ss") cls.client_arrow.get(str(val)).format("DD-MM-YYYY HH:mm:ss +0")
) )
elif isinstance(value_type, bool): elif isinstance(value_type, bool):
return True, bool(val) return True, bool(val)

View File

@ -122,6 +122,9 @@ class Users(CrudCollection, UserLoginModule, SelectAction):
person_uu_id: Mapped[str] = mapped_column( person_uu_id: Mapped[str] = mapped_column(
String, server_default="", comment="Person UUID", index=True String, server_default="", comment="Person UUID", index=True
) )
local_timezone = mapped_column(
String, server_default="GMT+3", comment="Local timezone of user"
)
person = relationship("People", back_populates="user", foreign_keys=[person_id]) person = relationship("People", back_populates="user", foreign_keys=[person_id])
@property @property
@ -183,11 +186,6 @@ class Users(CrudCollection, UserLoginModule, SelectAction):
@classmethod @classmethod
def credentials(cls): def credentials(cls):
person_object = People.filter_by_one(system=True, id=cls.person_id).data person_object = People.filter_by_one(system=True, id=cls.person_id).data
# if not person_object:
# raise HTTPException(
# status_code=401,
# detail="Person not found. Please contact the admin.",
# )
if person_object: if person_object:
return { return {
"person_id": person_object.id, "person_id": person_object.id,

View File

@ -15,6 +15,7 @@ from databases.sql_models.core_mixin import CrudCollection
class ApiEnumDropdown(CrudCollection): class ApiEnumDropdown(CrudCollection):
__tablename__ = "api_enum_dropdown" __tablename__ = "api_enum_dropdown"
__exclude__fields__ = ["enum_class"] __exclude__fields__ = ["enum_class"]
__language_model__ = None
id: Mapped[int] = mapped_column(primary_key=True) id: Mapped[int] = mapped_column(primary_key=True)
uu_id: Mapped[str] = mapped_column( uu_id: Mapped[str] = mapped_column(

View File

@ -1,7 +1,9 @@
from sqlalchemy import String, Boolean from sqlalchemy import String
from databases.sql_models.core_mixin import CrudCollection
from sqlalchemy.orm import mapped_column, Mapped from sqlalchemy.orm import mapped_column, Mapped
from databases.language_models.rules.rules import EndpointRestrictionLanguageModel
from databases.sql_models.core_mixin import CrudCollection
class EndpointRestriction(CrudCollection): class EndpointRestriction(CrudCollection):
""" """
@ -10,6 +12,7 @@ class EndpointRestriction(CrudCollection):
__tablename__ = "endpoint_restriction" __tablename__ = "endpoint_restriction"
__exclude__fields__ = [] __exclude__fields__ = []
__language_model__ = EndpointRestrictionLanguageModel
endpoint_function: Mapped[str] = mapped_column( endpoint_function: Mapped[str] = mapped_column(
String, server_default="", comment="Function name of the API endpoint" String, server_default="", comment="Function name of the API endpoint"

View File

@ -50,18 +50,9 @@ class FilterAttributes:
"""Save the data via the metadata.""" """Save the data via the metadata."""
try: try:
meta_data = getattr(cls, "meta_data", {}) meta_data = getattr(cls, "meta_data", {})
meta_data_created = meta_data.get("created", False) if meta_data.get("created", False):
if meta_data_created:
print("meta_data_created commit", meta_data_created)
cls.__session__.commit() cls.__session__.commit()
print("meta_data_created rollback", meta_data_created)
cls.__session__.rollback() cls.__session__.rollback()
# cls.raise_http_exception(
# status_code="HTTP_304_NOT_MODIFIED",
# error_case=meta_data.get("error_case", "Error on save and commit"),
# data={},
# message=meta_data.get("message", "Error on save and commit"),
# )
except SQLAlchemyError as e: except SQLAlchemyError as e:
cls.raise_http_exception( cls.raise_http_exception(
status_code="HTTP_304_NOT_MODIFIED", status_code="HTTP_304_NOT_MODIFIED",
@ -233,8 +224,9 @@ class FilterAttributes:
for smart_iter in cls.filter_expr(**filter_list.get("query", {})): for smart_iter in cls.filter_expr(**filter_list.get("query", {})):
if key := arg_left(smart_iter): if key := arg_left(smart_iter):
args = cls.add_new_arg_to_args(args, key, smart_iter) args = cls.add_new_arg_to_args(args, key, smart_iter)
query = cls._query().filter(*args) query = cls._query()
cls.total_count = query.count() cls.total_count = query.count()
query = query.filter(*args)
if cls.filter_attr: if cls.filter_attr:
data_query = cls.add_query_to_filter(query, filter_list) data_query = cls.add_query_to_filter(query, filter_list)
cls.filter_attr = None cls.filter_attr = None

View File

@ -157,6 +157,6 @@ def create_application_defaults_func(create_address=False):
if __name__ == "__main__": if __name__ == "__main__":
print("Service App Initial Default Runner is running") print("Service App Initial Default Runner is running")
# do_alembic() do_alembic()
create_application_defaults_func(create_address=True) # create_application_defaults_func(create_address=True)
print("Service App Initial Default Runner is completed") print("Service App Initial Default Runner is completed")