From fbd0e336e02d804527d84c631b51ca34a098fe36 Mon Sep 17 00:00:00 2001 From: berkay Date: Thu, 16 Jan 2025 19:49:21 +0300 Subject: [PATCH] error raise now locates loc --- ApiServices/AuthService/app.py | 24 +-- .../AuthService/application/authentication.py | 37 ++--- ApiServices/EventService/app.py | 29 ++-- ApiServices/EventService/routers/__init__.py | 2 - .../routers/validations/router.py | 123 ---------------- ApiServices/ValidationService/app.py | 30 ++-- .../ValidationService/routers/__init__.py | 4 +- .../routers/validations_by_endpoint/router.py | 139 ++++++++++++++++++ ApiServices/api_handlers/core_response.py | 83 ++++++++--- api-docker-compose.yml | 57 ++++--- api_events/events/abstract_class.py | 22 ++- api_events/events/building/building_build.py | 9 +- api_events/tasks2events/__init__.py | 4 +- api_objects/errors/alchemy_errors.py | 2 - api_objects/errors/errorHandlers.py | 49 +++--- api_objects/errors/errorMessages/__init__.py | 2 +- .../errors/errorMessages/baseErrorCluster.py | 111 +++++++------- .../building_responses.py | 32 +++- databases/sql_models/company/employee.py | 8 +- service_app_init/Dockerfile | 2 +- .../initialize_app/event_initator.py | 2 + service_app_init/runner.py | 4 +- 22 files changed, 438 insertions(+), 337 deletions(-) delete mode 100644 ApiServices/EventService/routers/validations/router.py create mode 100644 ApiServices/ValidationService/routers/validations_by_endpoint/router.py diff --git a/ApiServices/AuthService/app.py b/ApiServices/AuthService/app.py index 113cd16..818579e 100644 --- a/ApiServices/AuthService/app.py +++ b/ApiServices/AuthService/app.py @@ -6,7 +6,11 @@ from fastapi import Request, HTTPException, status 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 api_objects.errors.errorHandlers import ( + HTTPExceptionEvyos, + HTTPExceptionAnyHandler, + HTTPExceptionEvyosHandler, +) from middlewares.token_middleware import AuthHeaderMiddleware from application.create_file import create_app @@ -27,14 +31,16 @@ app.add_middleware( app.add_middleware(AuthHeaderMiddleware) # Initialize Exception and ExceptionInstance handlers -CustomExceptionHandler = HTTPExceptionEvyosHandler(**dict( - statuses=status, - exceptions=HTTPException, - response_model=JSONResponse, - exceptions_dict=EXCEPTION_DICTS, - errors_dict=ERRORS_DICT, - error_language_dict=ERRORS_LANG -)) +CustomExceptionHandler = HTTPExceptionEvyosHandler( + **dict( + statuses=status, + exceptions=HTTPException, + response_model=JSONResponse, + exceptions_dict=EXCEPTION_DICTS, + errors_dict=ERRORS_DICT, + error_language_dict=ERRORS_LANG, + ) +) CustomExceptionAnyHandler = HTTPExceptionAnyHandler(response_model=JSONResponse) # Register error handlers with bound methods diff --git a/ApiServices/AuthService/application/authentication.py b/ApiServices/AuthService/application/authentication.py index 7909178..4a820fb 100644 --- a/ApiServices/AuthService/application/authentication.py +++ b/ApiServices/AuthService/application/authentication.py @@ -1,11 +1,8 @@ -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, @@ -36,6 +33,13 @@ from api_validations.validations_response import ( 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 api_objects import ( + OccupantTokenObject, + EmployeeTokenObject, + CompanyToken, + OccupantToken, + HTTPExceptionEvyos, +) from databases import ( Companies, @@ -55,9 +59,7 @@ from databases import ( RelationshipEmployee2Build, ) -from api_services import ( - send_email, -) +from api_services import send_email class AuthenticationLoginEventMethods(MethodToEvent): @@ -74,26 +76,12 @@ class AuthenticationLoginEventMethods(MethodToEvent): @classmethod def authentication_login_with_domain_and_creds(cls, data: Login, request: Request): - from api_objects import HTTPExceptionEvyos - - 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( - # request, - # None, - # data.domain, - # data.access_key, - # success=False, - # error="Invalid credentials", - # ) return ResponseHandler.unauthorized("Invalid credentials") return ResponseHandler.success( message="User logged in successfully", @@ -104,11 +92,6 @@ class AuthenticationLoginEventMethods(MethodToEvent): "user": found_user.get_dict(), }, ) - # 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): @@ -128,9 +111,7 @@ class AuthenticationSelectEventMethods(MethodToEvent): cls, data: EmployeeSelection, token_dict: EmployeeTokenObject, request: Request ): """Handle employee company selection""" - Users.client_arrow = DateTimeLocal( - is_client=True, timezone=token_dict.local_timezone - ) + 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" diff --git a/ApiServices/EventService/app.py b/ApiServices/EventService/app.py index f60580f..818579e 100644 --- a/ApiServices/EventService/app.py +++ b/ApiServices/EventService/app.py @@ -5,9 +5,15 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi import Request, HTTPException, status 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 application.create_file import create_app -from api_objects.errors.errors_dictionary import ErrorHandlers from prometheus_fastapi_instrumentator import Instrumentator app = create_app(routers=routers) @@ -24,17 +30,22 @@ app.add_middleware( ) app.add_middleware(AuthHeaderMiddleware) -# Initialize error handlers -error_handlers = ErrorHandlers.create( - requests=Request, - exceptions=HTTPException, - response_model=JSONResponse, - status=status, +# Initialize Exception and ExceptionInstance handlers +CustomExceptionHandler = HTTPExceptionEvyosHandler( + **dict( + statuses=status, + exceptions=HTTPException, + response_model=JSONResponse, + exceptions_dict=EXCEPTION_DICTS, + errors_dict=ERRORS_DICT, + error_language_dict=ERRORS_LANG, + ) ) +CustomExceptionAnyHandler = HTTPExceptionAnyHandler(response_model=JSONResponse) # Register error handlers with bound methods -app.add_exception_handler(HTTPException, error_handlers.exception_handler_http) -app.add_exception_handler(Exception, error_handlers.exception_handler_exception) +app.add_exception_handler(HTTPExceptionEvyos, CustomExceptionHandler.handle_exception) +app.add_exception_handler(Exception, CustomExceptionAnyHandler.any_exception_handler) if __name__ == "__main__": uvicorn_config = { diff --git a/ApiServices/EventService/routers/__init__.py b/ApiServices/EventService/routers/__init__.py index c74a3c4..4e54a7a 100644 --- a/ApiServices/EventService/routers/__init__.py +++ b/ApiServices/EventService/routers/__init__.py @@ -58,7 +58,6 @@ from .decision_book.project_decision_book_items.router import ( from .decision_book.project_decision_book_person.router import ( build_decision_book_project_people_route, ) -from .validations.router import validations_route __all__ = [ "account_records_router", @@ -97,5 +96,4 @@ __all__ = [ "build_decision_book_project_route", "build_decision_book_project_items_route", "build_decision_book_project_people_route", - "validations_route", ] diff --git a/ApiServices/EventService/routers/validations/router.py b/ApiServices/EventService/routers/validations/router.py deleted file mode 100644 index cee20e6..0000000 --- a/ApiServices/EventService/routers/validations/router.py +++ /dev/null @@ -1,123 +0,0 @@ -from fastapi import status -from fastapi.routing import APIRouter -from fastapi.requests import Request -from fastapi.exceptions import HTTPException -from api_validations.validations_request import ( - EndpointValidation, -) -from pydantic import BaseModel - - -validations_route = APIRouter(prefix="/validations", tags=["Validations"]) -validations_route.include_router(validations_route, include_in_schema=True) - - -class EndpointValidationResponse(BaseModel): - language: str - headers: dict - validation: dict - - -class ValidationParser: - - def __init__(self, active_validation): - self.annotations = ( - active_validation.__annotations__.items() if active_validation else None - ) - self.schema = {} - self.parse() - - def parse(self): - for key, value in self.annotations or {}: - field_type, required = "string", False - if str(value) == "" or str(value) == "typing.Optional[str]": - field_type = "string" - required = not str(value) == "typing.Optional[str]" - elif str(value) == "" or str(value) == "typing.Optional[int]": - field_type = "integer" - required = not str(value) == "typing.Optional[int]" - elif ( - str(value) == "" or str(value) == "typing.Optional[bool]" - ): - field_type = "boolean" - required = not str(value) == "typing.Optional[bool]" - elif ( - str(value) == "" - or str(value) == "typing.Optional[float]" - ): - field_type = "float" - required = not str(value) == "typing.Optional[bool]" - elif ( - str(value) == "" - or str(value) == "typing.Optional[datetime.datetime]" - ): - field_type = "datetime" - required = not str(value) == "typing.Optional[datetime.datetime]" - self.schema[key] = {"type": field_type, "required": required} - - -def retrieve_validation_from_class(selected_event, events): - event_function_class = getattr(selected_event, "function_class", None) - event_function_code = getattr(selected_event, "function_code", None) - function_class = getattr(events, event_function_class, None) - return function_class.__event_validation__.get(event_function_code, None) - - -@validations_route.post(path="/endpoint", summary="Retrieve validation of endpoint") -def user_list(request: Request, validation: EndpointValidation): - import api_events.events as events - from api_services.redis.functions import get_object_via_access_key - from databases import ( - EndpointRestriction, - Events, - ) - - valid_token = get_object_via_access_key(request=request) - if not valid_token: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=f"No valid token found in the request.", - ) - endpoint_active = EndpointRestriction.filter_one( - EndpointRestriction.endpoint_name.ilike(f"%{str(validation.endpoint)}%"), - system=True, - ).data - if not endpoint_active: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=f"This endpoint {str(validation.endpoint)} is not active for this user, please contact your responsible company for further information.", - ) - if valid_token.user_type == 1 and not valid_token.selected_company: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Selected company is not found in the token object.", - ) - elif valid_token.user_type == 2 and not valid_token.selected_occupant: - raise HTTPException( - status_code=status.HTTP_418_IM_A_TEAPOT, - detail="Selected occupant is not found in the token object.", - ) - selected_event = Events.filter_one( - Events.endpoint_id == endpoint_active.id, - Events.id.in_(valid_token.selected_company.reachable_event_list_id), - ).data - if not selected_event: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="This endpoint requires event validation. Please contact your responsible company to use this event.", - ) - active_validation = retrieve_validation_from_class(selected_event, events) - if not active_validation: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="No validation found for this endpoint.", - ) - headers = getattr( - active_validation, str(valid_token.lang).lower(), active_validation.tr - ) - validation_parse = ValidationParser(active_validation=active_validation) - return EndpointValidationResponse( - language=valid_token.lang, - headers=headers, - validation=validation_parse.schema, - ) diff --git a/ApiServices/ValidationService/app.py b/ApiServices/ValidationService/app.py index 219f619..818579e 100644 --- a/ApiServices/ValidationService/app.py +++ b/ApiServices/ValidationService/app.py @@ -5,12 +5,17 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi import Request, HTTPException, status 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 application.create_file import create_app -from api_objects.errors.errors_dictionary import ErrorHandlers from prometheus_fastapi_instrumentator import Instrumentator - app = create_app(routers=routers) Instrumentator().instrument(app=app).expose(app=app) @@ -25,17 +30,22 @@ app.add_middleware( ) app.add_middleware(AuthHeaderMiddleware) -# Initialize error handlers -error_handlers = ErrorHandlers.create( - requests=Request, - exceptions=HTTPException, - response_model=JSONResponse, - status=status, +# Initialize Exception and ExceptionInstance handlers +CustomExceptionHandler = HTTPExceptionEvyosHandler( + **dict( + statuses=status, + exceptions=HTTPException, + response_model=JSONResponse, + exceptions_dict=EXCEPTION_DICTS, + errors_dict=ERRORS_DICT, + error_language_dict=ERRORS_LANG, + ) ) +CustomExceptionAnyHandler = HTTPExceptionAnyHandler(response_model=JSONResponse) # Register error handlers with bound methods -app.add_exception_handler(HTTPException, error_handlers.exception_handler_http) -app.add_exception_handler(Exception, error_handlers.exception_handler_exception) +app.add_exception_handler(HTTPExceptionEvyos, CustomExceptionHandler.handle_exception) +app.add_exception_handler(Exception, CustomExceptionAnyHandler.any_exception_handler) if __name__ == "__main__": uvicorn_config = { diff --git a/ApiServices/ValidationService/routers/__init__.py b/ApiServices/ValidationService/routers/__init__.py index a9a2c5b..6743d15 100644 --- a/ApiServices/ValidationService/routers/__init__.py +++ b/ApiServices/ValidationService/routers/__init__.py @@ -1 +1,3 @@ -__all__ = [] +from .validations_by_endpoint.router import validations_router + +__all__ = ["validations_router"] diff --git a/ApiServices/ValidationService/routers/validations_by_endpoint/router.py b/ApiServices/ValidationService/routers/validations_by_endpoint/router.py new file mode 100644 index 0000000..750d416 --- /dev/null +++ b/ApiServices/ValidationService/routers/validations_by_endpoint/router.py @@ -0,0 +1,139 @@ +import json + +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import EndpointValidation +from ApiServices.api_handlers.core_response import HTTPExceptionEvyos +from pydantic import BaseModel + + +validations_router = APIRouter(prefix="/validations", tags=["Validations"]) +validations_router.include_router(validations_router, include_in_schema=True) + + +class EndpointValidationResponse(BaseModel): + language: str + headers: dict + validation: dict + + +class ValidationParser: + + def __init__(self, active_validation): + self.core_validation = active_validation + self.annotations = ( + active_validation.model_json_schema() if active_validation else None + ) + self.annotations = json.loads(json.dumps(self.annotations)) + self.schema = {} + self.parse() + + def parse(self): + from api_validations.validations_request import PydanticBaseModel, CrudRecords + properties = self.annotations.get("properties").items() + for key, value in properties: + default, title, type_field = value.get("default", None), value.get("title", ""), value.get("type", None) + field_type, required, possible_types = "string", False, [] + if not type_field: + for _ in value.get("anyOf") or []: + type_opt = json.loads(json.dumps(_)) + if not type_opt.get("type") == "null": + possible_types.append(type_opt.get("type")) + field_type = possible_types[0] + else: + field_type, required = type_field, True + total_class_annotations = { + **self.core_validation.__annotations__ + **PydanticBaseModel.__annotations__, + **CrudRecords.__annotations__, + } + attribute_of_class = total_class_annotations.get(key, None) + if ( + str(attribute_of_class) == "" + or str(attribute_of_class) == "typing.Optional[str]" + ): + field_type = "string" + required = not str(attribute_of_class) == "typing.Optional[str]" + elif ( + str(attribute_of_class) == "" + or str(attribute_of_class) == "typing.Optional[int]" + ): + field_type = "integer" + required = not str(attribute_of_class) == "typing.Optional[int]" + elif ( + str(attribute_of_class) == "" + or str(attribute_of_class) == "typing.Optional[bool]" + ): + field_type = "boolean" + required = not str(attribute_of_class) == "typing.Optional[bool]" + elif ( + str(attribute_of_class) == "" + or str(attribute_of_class) == "typing.Optional[float]" + ): + field_type = "float" + required = not str(attribute_of_class) == "typing.Optional[bool]" + elif ( + str(attribute_of_class) == "" + or str(attribute_of_class) == "typing.Optional[datetime.datetime]" + ): + field_type = "datetime" + required = ( + not str(attribute_of_class) == "typing.Optional[datetime.datetime]" + ) + self.schema[key] = {"type": field_type, "required": required, "default": default} + + @classmethod + def retrieve_validation_from_class(cls, selected_event, events, lang): + event_function_class = getattr(selected_event, "function_class", None) + event_function_code = getattr(selected_event, "function_code", None) + function_class = getattr(events, event_function_class, None) + event_headers = function_class.retrieve_language_parameters( + language=lang, function_code=event_function_code + ) + event_validation = function_class.__event_validation__.get( + event_function_code, [None] + )[0] + if not event_validation: + raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=lang) + return event_validation, event_headers + + +@validations_router.post(path="/validations", summary="Get validations by endpoint") +def validation_by_endpoint(request: Request, validation: EndpointValidation): + import api_events.events as events + + from api_services.redis.functions import RedisActions + from databases import EndpointRestriction, Events + + valid_token = RedisActions.get_object_via_access_key(request=request) + language = str(valid_token.lang).lower() + if not valid_token: + raise HTTPExceptionEvyos(error_code="", lang=language) + endpoint_active = EndpointRestriction.filter_one( + EndpointRestriction.endpoint_name.ilike(f"%{str(validation.endpoint)}%"), + system=True, + ).data + if not endpoint_active: + raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language) + if valid_token.user_type == 1 and not valid_token.selected_company: + raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language) + elif valid_token.user_type == 2 and not valid_token.selected_occupant: + raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language) + selected_event = Events.filter_one( + Events.endpoint_id == endpoint_active.id, + Events.id.in_(valid_token.selected_company.reachable_event_list_id), + ).data + if not selected_event: + raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language) + active_validation, active_headers = ValidationParser.retrieve_validation_from_class( + selected_event=selected_event, events=events, lang=language + ) + if not active_validation: + raise HTTPExceptionEvyos(error_code="HTTP_400_BAD_REQUEST", lang=language) + validation_parse = ValidationParser(active_validation=active_validation) + return EndpointValidationResponse( + language=language, + headers=active_headers, + validation=validation_parse.schema, + ) diff --git a/ApiServices/api_handlers/core_response.py b/ApiServices/api_handlers/core_response.py index ee9cca7..cd173a8 100644 --- a/ApiServices/api_handlers/core_response.py +++ b/ApiServices/api_handlers/core_response.py @@ -6,8 +6,8 @@ from pydantic import BaseModel from api_validations.validations_request import PydanticBaseModel, BaseModelRegular from databases.sql_models.response_model import AlchemyResponse from sqlalchemy.orm import Query +from api_objects.errors.errorHandlers import HTTPExceptionEvyos -MODEL_TYPE = Callable[[Any], Any] class Pagination: size: int = 10 @@ -18,12 +18,24 @@ class Pagination: totalCount: int = 1 totalPage: int = 1 - def change(self, page=None, size=None, order_field=None, order_type=None): + def change( + self, + page: int = 1, + size: int = 10, + order_field: str = "id", + order_type: str = "asc", + ): + self.size = size if 10 < size < 40 else 10 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() + if self.page > self.totalPage: + self.page = self.totalPage + elif self.page < 1: + self.page = 1 + self.setter_page() def feed(self, data): if isinstance(data, list): @@ -32,14 +44,16 @@ class Pagination: self.totalCount = data.count elif isinstance(data, Query): self.totalCount = data.count() + self.setter_page() 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 + remainder = self.totalCount % self.size + if remainder > 0: self.totalPage = int(round(self.totalCount / self.size, 0)) + 1 + if self.page == self.totalPage: + self.pageCount = remainder def as_dict(self): return { @@ -56,17 +70,19 @@ class Pagination: class SingleAlchemyResponse: status_code = "HTTP_200_OK" result: AlchemyResponse - response_model: MODEL_TYPE + response_model: Any message: str completed: bool + cls_object: Any = (None,) def __new__( cls, message: str, - response_model: MODEL_TYPE, + response_model: Any, status_code: str = "HTTP_200_OK", result: AlchemyResponse = None, completed: bool = True, + cls_object: Any = None, ): cls.status_code = getattr(status, status_code, "HTTP_200_OK") cls.message = message @@ -75,10 +91,16 @@ class SingleAlchemyResponse: cls.response_model = response_model if not isinstance(cls.result, AlchemyResponse): - raise Exception("Invalid response type 4 single alchemy response") + HTTPExceptionEvyos( + lang=cls_object.lang, + error_code="HTTP_400_BAD_REQUEST", + ) if not cls.result.first: - raise Exception("Invalid data type 4 single alchemy response") + HTTPExceptionEvyos( + lang=cls_object.lang, + error_code="HTTP_400_BAD_REQUEST", + ) pagination = Pagination() pagination.change(page=1) @@ -103,7 +125,7 @@ class AlchemyJsonResponse: result: AlchemyResponse completed: bool filter_attributes: Any = None - response_model: MODEL_TYPE = None + response_model: Any = None cls_object: Any = None def __new__( @@ -112,7 +134,7 @@ class AlchemyJsonResponse: status_code: str = "HTTP_200_OK", result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None, completed: bool = True, - response_model: MODEL_TYPE = None, + response_model: Any = None, cls_object: Any = None, filter_attributes: Any = None, ): @@ -126,11 +148,15 @@ class AlchemyJsonResponse: pagination = Pagination() if cls.result.first: - raise Exception("Invalid data type 4 alchemy response") + HTTPExceptionEvyos( + lang=cls_object.lang, + error_code="HTTP_400_BAD_REQUEST", + ) if filter_attributes: pagination.change( - page=filter_attributes.page, size=filter_attributes.size, + page=filter_attributes.page, + size=filter_attributes.size, order_field=filter_attributes.order_field, order_type=filter_attributes.order_type, ) @@ -158,8 +184,8 @@ class ListJsonResponse: message: str completed: bool filter_attributes: Any - response_model: MODEL_TYPE = None, - cls_object: Any = None, + response_model: Any = (None,) + cls_object: Any = (None,) def __new__( cls, @@ -167,7 +193,7 @@ class ListJsonResponse: status_code: str = "HTTP_200_OK", result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None, completed: bool = True, - response_model: MODEL_TYPE = None, + response_model: Any = None, cls_object: Any = None, filter_attributes: Any = None, ): @@ -176,15 +202,20 @@ class ListJsonResponse: cls.result = result cls.completed = completed cls.filter_attributes = filter_attributes - cls.response_model: MODEL_TYPE = response_model + cls.response_model: Any = response_model if not isinstance(cls.result, list): - raise Exception("Invalid data type 4 list json response") + HTTPExceptionEvyos( + lang=cls_object.lang, + error_code="HTTP_400_BAD_REQUEST", + ) 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] + data = [ + cls.response_model(**data_object).dump() for data_object in cls.result + ] pagination.feed(data) return JSONResponse( status_code=cls.status_code, @@ -196,14 +227,15 @@ class ListJsonResponse: ), ) + 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, + response_model: Any = (None,) + cls_object: Any = (None,) def __new__( cls, @@ -211,7 +243,7 @@ class DictJsonResponse: status_code: str = "HTTP_200_OK", result: Union[BaseModelRegular, BaseModel, PydanticBaseModel] = None, completed: bool = True, - response_model: MODEL_TYPE = None, + response_model: Any = None, cls_object: Any = None, filter_attributes: Any = None, ): @@ -220,10 +252,13 @@ class DictJsonResponse: cls.result = result cls.completed = completed cls.filter_attributes = filter_attributes - cls.response_model: MODEL_TYPE = response_model + cls.response_model: Any = response_model if not isinstance(cls.result, dict): - raise Exception("Invalid data type 4 dict json response") + HTTPExceptionEvyos( + lang=cls_object.lang, + error_code="HTTP_400_BAD_REQUEST", + ) pagination = Pagination() pagination.change(page=1) diff --git a/api-docker-compose.yml b/api-docker-compose.yml index 320ce82..0976315 100644 --- a/api-docker-compose.yml +++ b/api-docker-compose.yml @@ -1,4 +1,5 @@ services: + wag_management_auth_service: container_name: wag_management_auth_service # restart: on-failure @@ -13,41 +14,39 @@ services: - auth_venv:/service_app/.venv - auth_logs:/service_app/logs + wag_management_validation_service: + container_name: wag_management_validation_service + # restart: on-failure + build: + context: . + dockerfile: ApiServices/ValidationService/Dockerfile + ports: + - "1113:41575" + environment: + - PYTHONPATH=/service_app + volumes: + - validation_venv:/service_app/.venv + - validation_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: - # container_name: wag_management_event_service - # # restart: on-failure - # build: - # context: . - # dockerfile: ApiServices/EventService/Dockerfile - # ports: - # - "1112:41575" - # environment: - # - PYTHONPATH=/service_app - # volumes: - # - event_venv:/service_app/.venv - # - event_logs:/service_app/logs - - # wag_management_validation_service: - # container_name: wag_management_validation_service - # # restart: on-failure - # build: - # context: . - # dockerfile: ApiServices/ValidationService/Dockerfile - # ports: - # - "1113:41575" - # environment: - # - PYTHONPATH=/service_app - # volumes: - # - validation_venv:/service_app/.venv - # - validation_logs:/service_app/logs - - +# wag_management_event_service: +# container_name: wag_management_event_service +# # restart: on-failure +# build: +# context: . +# dockerfile: ApiServices/EventService/Dockerfile +# ports: +# - "1112:41575" +# environment: +# - PYTHONPATH=/service_app +# volumes: +# - event_venv:/service_app/.venv +# - event_logs:/service_app/logs volumes: auth_venv: diff --git a/api_events/events/abstract_class.py b/api_events/events/abstract_class.py index 03d8132..0ca2f9c 100644 --- a/api_events/events/abstract_class.py +++ b/api_events/events/abstract_class.py @@ -86,7 +86,7 @@ class MethodToEvent(ABC, ActionsSchemaFactory): event_category: str = "" __event_keys__: Dict[str, str] = {} - __event_validation__: Dict[str, Any] = {} + __event_validation__: list[Any, list[Any]] = [] @classmethod def call_event_method(cls, method_uu_id: str, *args: Any, **kwargs: Any) -> Any: @@ -106,7 +106,6 @@ class MethodToEvent(ABC, ActionsSchemaFactory): function_name = cls.__event_keys__.get(method_uu_id) if not function_name: raise AttributeError(f"No method found for UUID: {method_uu_id}") - return getattr(cls, function_name)(*args, **kwargs) @classmethod @@ -128,3 +127,22 @@ class MethodToEvent(ABC, ActionsSchemaFactory): status_code=status.HTTP_406_NOT_ACCEPTABLE, detail=f"No {user_type} can reach this event. A notification has been sent to admin.", ) + + @classmethod + def retrieve_language_parameters(cls, language: str, function_code: str): + + event_response_model = dict(cls.__event_validation__).get(function_code)[0] + event_language_models = list( + dict(cls.__event_validation__).get(function_code)[1] + ) + language_models, language_response = {}, {} + for event_language_model in event_language_models: + language_models.update({**event_language_model.get(language, "tr")}) + from api_validations.validations_response.building_responses import ( + ListBuildingResponse, + ) + + for model_field in event_response_model.model_fields: + if model_field in language_models: + language_response[model_field] = language_models[model_field] + return language_response diff --git a/api_events/events/building/building_build.py b/api_events/events/building/building_build.py index d461c7f..2f9c0f9 100644 --- a/api_events/events/building/building_build.py +++ b/api_events/events/building/building_build.py @@ -18,10 +18,10 @@ from api_validations.validations_request import ( PatchRecord, ListOptions, ) -from api_validations.validations_response import BuildResponse from ApiServices.api_handlers import AlchemyJsonResponse from api_events.events.abstract_class import MethodToEvent, ActionsSchema from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject +from api_validations.validations_response.building_responses import ListBuildingResponse class BuildListEventMethods(MethodToEvent): @@ -34,7 +34,10 @@ class BuildListEventMethods(MethodToEvent): "68b3b5ed-b74c-4a27-820f-3959214e94e9": "build_list", } __event_validation__ = { - "68b3b5ed-b74c-4a27-820f-3959214e94e9": BuildResponse, + "68b3b5ed-b74c-4a27-820f-3959214e94e9": ( + ListBuildingResponse, + [Build.__language_model__], + ) } @classmethod @@ -59,7 +62,7 @@ class BuildListEventMethods(MethodToEvent): result=records, cls_object=Build, filter_attributes=list_options, - response_model=BuildResponse, + response_model=ListBuildingResponse, ) diff --git a/api_events/tasks2events/__init__.py b/api_events/tasks2events/__init__.py index c6f44bb..ff8a6e3 100644 --- a/api_events/tasks2events/__init__.py +++ b/api_events/tasks2events/__init__.py @@ -1,4 +1,4 @@ -from api_events.tasks2events.common_tasks.default_user import AuthDefaultEventBlock +# from api_events.tasks2events.common_tasks.default_user import AuthDefaultEventBlock from api_events.tasks2events.employee_tasks.super_user import SuperUserEventBlock from api_events.tasks2events.occupant_tasks.build_manager import BuildManager @@ -29,7 +29,7 @@ from api_events.tasks2events.occupant_tasks.project_responsiable import ( __all__ = [ - "AuthDefaultEventBlock", + # "AuthDefaultEventBlock", "SuperUserEventBlock", "BuildManager", "BuildOwner", diff --git a/api_objects/errors/alchemy_errors.py b/api_objects/errors/alchemy_errors.py index cf5de2a..b759097 100644 --- a/api_objects/errors/alchemy_errors.py +++ b/api_objects/errors/alchemy_errors.py @@ -1,5 +1,3 @@ - - class HTTPExceptionError: def __init__(self, lang): diff --git a/api_objects/errors/errorHandlers.py b/api_objects/errors/errorHandlers.py index c9897f7..2b7b0ba 100644 --- a/api_objects/errors/errorHandlers.py +++ b/api_objects/errors/errorHandlers.py @@ -1,46 +1,42 @@ 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 __init__( + self, + **kwargs, + ): + self.EXCEPTIONS = kwargs.get( + "exceptions" + ) # from fastapi.exceptions import HTTPException + self.STATUSES = kwargs.get("statuses") # from fastapi import status + self.EXCEPTION_DICTS: dict = kwargs.get("exceptions_dict") + self.ERRORS_DICT: dict = kwargs.get("errors_dict") + self.ERRORS_LANG: dict = kwargs.get("error_language_dict") + self.RESPONSE_MODEL: Any = kwargs.get("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")) + 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 + return message_by_lang.get(str(exc.error_code).upper(), "Unknown error") 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( @@ -48,13 +44,14 @@ class HTTPExceptionEvyosHandler: 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}, + content={"message": str(exc), "lang": "en"}, ) diff --git a/api_objects/errors/errorMessages/__init__.py b/api_objects/errors/errorMessages/__init__.py index 99b03d1..6b72e69 100644 --- a/api_objects/errors/errorMessages/__init__.py +++ b/api_objects/errors/errorMessages/__init__.py @@ -10,7 +10,7 @@ ERRORS_LANG = { }, "en": { **BASE_ERROR_LANGUAGE["en"], - } + }, } ERRORS_DICT = { **BASE_ERRORS, diff --git a/api_objects/errors/errorMessages/baseErrorCluster.py b/api_objects/errors/errorMessages/baseErrorCluster.py index 0cff5c4..cdd9932 100644 --- a/api_objects/errors/errorMessages/baseErrorCluster.py +++ b/api_objects/errors/errorMessages/baseErrorCluster.py @@ -1,4 +1,3 @@ - BASE_ERRORS = { "NOT_CREATED": 405, "NOT_DELETED": 405, @@ -46,59 +45,59 @@ BASE_ERROR_LANGUAGE = { } 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", - } +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", +} diff --git a/api_validations/validations_response/building_responses.py b/api_validations/validations_response/building_responses.py index dcbd9fd..b639f6e 100644 --- a/api_validations/validations_response/building_responses.py +++ b/api_validations/validations_response/building_responses.py @@ -1,9 +1,35 @@ -from pydantic import BaseModel -from typing import Optional, List +from typing import Optional, List, Generic from datetime import datetime from uuid import UUID from decimal import Decimal -from .base_responses import BaseResponse, CrudCollection + +from api_validations.validations_response.base_responses import ( + BaseResponse, + CrudCollection, +) +from api_validations.validations_request import PydanticBaseModel + + +class ListBuildingResponse(PydanticBaseModel): + + gov_address_code: str + build_name: str + build_types_uu_id: Optional[str] = None + build_no: Optional[str] = None + max_floor: Optional[int] = None + underground_floor: Optional[int] = None + address_uu_id: Optional[str] = None + build_date: Optional[str] = None + decision_period_date: Optional[str] = None + tax_no: Optional[str] = None + lift_count: Optional[int] = None + heating_system: Optional[bool] = None + cooling_system: Optional[bool] = None + hot_water_system: Optional[bool] = None + block_service_man_count: Optional[int] = None + security_service_man_count: Optional[int] = None + garage_count: Optional[int] = None + site_uu_id: Optional[str] = None class BuildAreaListResponse(BaseResponse): diff --git a/databases/sql_models/company/employee.py b/databases/sql_models/company/employee.py index a9fb8f2..8411ddf 100644 --- a/databases/sql_models/company/employee.py +++ b/databases/sql_models/company/employee.py @@ -7,10 +7,10 @@ from sqlalchemy import ( from sqlalchemy.orm import mapped_column, Mapped from databases.language_models.company.employee import ( -StaffLanguageModel, -EmployeesLanguageModel, -EmployeeHistoryLanguageModel, -EmployeesSalariesLanguageModel, + StaffLanguageModel, + EmployeesLanguageModel, + EmployeeHistoryLanguageModel, + EmployeesSalariesLanguageModel, ) from databases.sql_models.core_mixin import CrudCollection diff --git a/service_app_init/Dockerfile b/service_app_init/Dockerfile index 4c10dc7..4f4da56 100644 --- a/service_app_init/Dockerfile +++ b/service_app_init/Dockerfile @@ -22,7 +22,7 @@ COPY api_services ./service_app_init/api_services COPY ApiServices ./service_app_init/ApiServices COPY ApiServices/EventService/routers ./service_app_init/routers -#COPY ../service_app/application ./service_app_init/application +COPY ApiServices/EventService/application ./service_app_init/application WORKDIR /service_app_init diff --git a/service_app_init/initialize_app/event_initator.py b/service_app_init/initialize_app/event_initator.py index cfc6393..8745ac5 100644 --- a/service_app_init/initialize_app/event_initator.py +++ b/service_app_init/initialize_app/event_initator.py @@ -82,7 +82,9 @@ def add_events_all_services_and_occupant_types(): import api_events.tasks2events as tasks2events for event_block in tasks2events.__all__: + print("event_block", event_block) event_block_class = getattr(tasks2events, event_block) + print("event_block_class", event_block_class) service_selected = Services.filter_one( Services.service_code == getattr(event_block_class, "service_code", None), system=True, diff --git a/service_app_init/runner.py b/service_app_init/runner.py index 790d0d3..0d7c0e6 100644 --- a/service_app_init/runner.py +++ b/service_app_init/runner.py @@ -157,6 +157,6 @@ def create_application_defaults_func(create_address=False): if __name__ == "__main__": print("Service App Initial Default Runner is running") - do_alembic() - # create_application_defaults_func(create_address=True) + # do_alembic() + create_application_defaults_func(create_address=False) print("Service App Initial Default Runner is completed")