From 5223f36da71143da48ffd3797d9ebf9786642ab5 Mon Sep 17 00:00:00 2001 From: berkay Date: Mon, 23 Dec 2024 13:07:25 +0300 Subject: [PATCH] events updated --- .../later_use_codes}/authentication.py | 0 .../later_use_codes}/events_bind_events.py | 1 + api_configs/configs.py | 3 +- api_events/events/__init__.py | 4 +- .../events/application/authentication.py | 114 +++++++++++++----- .../decision_book_decision_book_person.py | 2 +- .../events/events/events_bind_services.py | 1 - api_events/events/events/events_events.py | 6 +- api_services/redis/auth_actions/token.py | 4 +- .../validations_request/building.py | 7 +- .../core_request_validations.py | 4 +- api_validations/validations_request/events.py | 4 +- databases/sql_models/building/build.py | 14 +-- databases/sql_models/identity/identity.py | 4 +- service_app/routers/authentication/router.py | 4 +- service_app/routers/validations/router.py | 42 ++++++- service_app_banks/isbank/isbank_sender.py | 11 +- 17 files changed, 160 insertions(+), 65 deletions(-) rename {api_events/events => a_project_files/later_use_codes}/authentication.py (100%) rename {api_events/events/events => a_project_files/later_use_codes}/events_bind_events.py (99%) diff --git a/api_events/events/authentication.py b/a_project_files/later_use_codes/authentication.py similarity index 100% rename from api_events/events/authentication.py rename to a_project_files/later_use_codes/authentication.py diff --git a/api_events/events/events/events_bind_events.py b/a_project_files/later_use_codes/events_bind_events.py similarity index 99% rename from api_events/events/events/events_bind_events.py rename to a_project_files/later_use_codes/events_bind_events.py index 6b3417d..4a4d207 100644 --- a/api_events/events/events/events_bind_events.py +++ b/a_project_files/later_use_codes/events_bind_events.py @@ -19,6 +19,7 @@ from api_validations.core_response import AlchemyJsonResponse class EventBindOccupantEventMethods(MethodToEvent): + event_type = "UPDATE" __event_keys__ = { "5702f0a9-fe8f-4aae-922e-6e04b497ef6a": "bind_events_occupant_super_user", diff --git a/api_configs/configs.py b/api_configs/configs.py index b4ba886..1df9fd0 100644 --- a/api_configs/configs.py +++ b/api_configs/configs.py @@ -28,11 +28,10 @@ class Config: "/authentication/create_password", "/authentication/reset_password", "/authentication/forgot", - "/authentication/avatar", "/authentication/valid", "/api/Contact/Us/current_date", ] - NOT_SECURE_PATHS = ["/access/endpoints/available", "/validations/endpoint"] + NOT_SECURE_PATHS = ["/access/endpoints/available", "/validations/endpoint", "/authentication/avatar"] APP_NAME = "evyos-web-api-gateway" TITLE = "WAG API Web Api Gateway" diff --git a/api_events/events/__init__.py b/api_events/events/__init__.py index a09aefa..744f89d 100644 --- a/api_events/events/__init__.py +++ b/api_events/events/__init__.py @@ -14,7 +14,7 @@ from api_events.events.address.address import ( AddressPostCodeUpdateEventMethod, AddressPostCodeListEventMethod, ) -from api_events.events.authentication import ( +from api_events.events.application.authentication import ( AuthenticationLoginEventMethod, AuthenticationSelectEventMethod, AuthenticationCheckTokenEventMethod, @@ -143,7 +143,7 @@ from api_events.events.decision_book.project_decision_book_items import ( BuildDecisionBookProjectItemsCreateEventMethod, BuildDecisionBookProjectItemsListEventMethod, ) -from api_events.events.events.events_bind_events import ( +from a_project_files.later_use_codes.events_bind_events import ( EventBindOccupantEventMethod, EventBindEmployeeEventMethod, ) diff --git a/api_events/events/application/authentication.py b/api_events/events/application/authentication.py index 6e08905..9439f80 100644 --- a/api_events/events/application/authentication.py +++ b/api_events/events/application/authentication.py @@ -1,7 +1,9 @@ +import datetime import json import typing from typing import Union +import arrow from fastapi import status from fastapi.requests import Request from fastapi.exceptions import HTTPException @@ -116,9 +118,11 @@ class AuthenticationSelectEventMethods(MethodToEvent): def authentication_select_company_or_occupant_type( cls, request: Request, - data, - token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject], + data: Union[EmployeeSelection, OccupantSelection], + token_dict: Union[EmployeeTokenObject, OccupantTokenObject], ): + from api_objects import OccupantToken, CompanyToken + if isinstance(token_dict, EmployeeTokenObject): if data.company_uu_id not in token_dict.companies_uu_id_list: return JSONResponse( @@ -141,7 +145,6 @@ class AuthenticationSelectEventMethods(MethodToEvent): duties.id for duties in Duties.filter_all( Duties.company_id == selected_company.id, - Duties.department_id.in_(department_ids), ).data ] staff_ids = [ @@ -154,7 +157,6 @@ class AuthenticationSelectEventMethods(MethodToEvent): Employees.people_id == token_dict.person_id, Employees.staff_id.in_(staff_ids), ).data - reachable_event_list_id = Event2Employee.get_event_id_by_employee_id( employee_id=employee.id ) @@ -167,12 +169,11 @@ class AuthenticationSelectEventMethods(MethodToEvent): department = Departments.filter_one( Departments.id == duties.department_id, ).data - bulk_id = Duty.filter_one( - Duty.duty_code == "BULK", - ).data - bulk_duty_id = Duties.filter_one( - Duties.company_id == selected_company.id, - Duties.duties_id == bulk_id.id, + 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 update_selected_to_redis( request=request, @@ -189,7 +190,6 @@ class AuthenticationSelectEventMethods(MethodToEvent): employee_id=employee.id, employee_uu_id=employee.uu_id.__str__(), reachable_event_list_id=reachable_event_list_id, - # reachable_event_list_uu_id=reachable_event_list_uu_id, ), ) return JSONResponse( @@ -200,23 +200,25 @@ class AuthenticationSelectEventMethods(MethodToEvent): status_code=status.HTTP_200_OK, ) elif isinstance(token_dict, OccupantTokenObject): - occupant_type = OccupantTypes.filter_one( - OccupantTypes.uu_id == data.occupant_uu_id + occupant_type = OccupantTypes.filter_by_one( + system=True, uu_id=data.occupant_uu_id ).data if not occupant_type: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Occupant Type is not found", ) - build_part = BuildParts.filter_one( - BuildParts.uu_id == data.build_part_uu_id, + build_part = BuildParts.filter_by_one( + system=True, uu_id=data.build_part_uu_id ).data if not build_part: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Build Part is not found", ) - build = Build.filter_one(Build.id == build_part.build_id).data + build = Build.filter_one( + Build.id == build_part.build_id, + ).data related_company = RelationshipEmployee2Build.filter_one( RelationshipEmployee2Build.member_id == build.id, ).data @@ -233,8 +235,7 @@ class AuthenticationSelectEventMethods(MethodToEvent): ).data: reachable_event_list_id = ( Event2Occupant.get_event_id_by_build_living_space_id( - Event2Occupant.build_living_space_id - == selected_occupant_type.id + build_living_space_id=selected_occupant_type.id ) ) update_selected_to_redis( @@ -254,7 +255,6 @@ class AuthenticationSelectEventMethods(MethodToEvent): responsible_company_id=company_related.id, responsible_company_uuid=company_related.uu_id.__str__(), reachable_event_list_id=reachable_event_list_id, - # reachable_event_list_uu_id=reachable_event_list_uu_id, ), ) return JSONResponse( @@ -270,6 +270,7 @@ class AuthenticationSelectEventMethods(MethodToEvent): ) + class AuthenticationCheckTokenEventMethods(MethodToEvent): event_type = "LOGIN" @@ -284,10 +285,9 @@ class AuthenticationCheckTokenEventMethods(MethodToEvent): } @classmethod - def authentication_login_with_domain_and_creds( - cls, - request, - token_dict: typing.Union[EmployeeSelection, OccupantSelection], + def authentication_check_token_is_valid( + cls, + request, ): if get_object_via_access_key(request=request): return JSONResponse( @@ -652,6 +652,52 @@ class AuthenticationForgotPasswordEventMethods(MethodToEvent): ) +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 JSONResponse( + content={ + "completed": True, + "message": "Password change link is sent to your email or phone", + "data": found_user.get_dict(), + }, + status_code=status.HTTP_200_OK, + ) + + class AuthenticationDownloadAvatarEventMethods(MethodToEvent): event_type = "LOGIN" @@ -666,27 +712,28 @@ class AuthenticationDownloadAvatarEventMethods(MethodToEvent): } @classmethod - def authentication_download_avatar(cls, data: Forgot): - if found_user := Users.check_user_exits( - access_key=data.access_key, domain=data.domain - ): + 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 = int( - system_arrow.now() - system_arrow.get(str(found_user.expiry_ends)).days - ) + expired_int = ( + system_arrow.now() - system_arrow.get(str(found_user.expiry_ends)) + ).days + return JSONResponse( content={ "completed": True, "message": "Avatar and profile is shared via user credentials", "data": { - "last_seen": str(found_user.last_seen), + "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": expired_int, + "expired_int": int(expired_int), }, }, status_code=status.HTTP_200_OK, @@ -730,6 +777,9 @@ AuthenticationForgotPasswordEventMethod = AuthenticationForgotPasswordEventMetho AuthenticationDownloadAvatarEventMethod = AuthenticationDownloadAvatarEventMethods( action=ActionsSchema(endpoint="/authentication/avatar") ) +AuthenticationResetPasswordEventMethod = AuthenticationResetPasswordEventMethods( + action=ActionsSchema(endpoint="/authentication/reset_password") +) # UserLogger.log_error( # str( diff --git a/api_events/events/decision_book/decision_book_decision_book_person.py b/api_events/events/decision_book/decision_book_decision_book_person.py index 74d96f3..adc80e3 100644 --- a/api_events/events/decision_book/decision_book_decision_book_person.py +++ b/api_events/events/decision_book/decision_book_decision_book_person.py @@ -38,7 +38,7 @@ class DecisionBookPersonListEventMethods(MethodToEvent): @classmethod def building_decision_book_person_list( - cls, data: ListOptions, token_dict: EmployeeTokenObject + cls, data: ListOptions, token_dict: typing.Union[EmployeeTokenObject, OccupantTokenObject], ): return diff --git a/api_events/events/events/events_bind_services.py b/api_events/events/events/events_bind_services.py index b32ec30..e5997e2 100644 --- a/api_events/events/events/events_bind_services.py +++ b/api_events/events/events/events_bind_services.py @@ -81,7 +81,6 @@ class ServiceBindOccupantEventMethods(MethodToEvent): occupants_build_part = BuildParts.filter_one( BuildParts.uu_id == data.build_part_uu_id, BuildParts.build_id == token_dict.selected_occupant.build_id, - BuildParts.active == True, ).data if not occupants_build_part: return JSONResponse( diff --git a/api_events/events/events/events_events.py b/api_events/events/events/events_events.py index ab3ed81..5560e5f 100644 --- a/api_events/events/events/events_events.py +++ b/api_events/events/events/events_events.py @@ -50,7 +50,7 @@ class EventsListEventMethods(MethodToEvent): ) return AlchemyJsonResponse( completed=True, - message="DecisionBook are listed successfully", + message="Events are listed successfully", result=records, ) elif isinstance(token_dict, EmployeeTokenObject): @@ -62,12 +62,12 @@ class EventsListEventMethods(MethodToEvent): ) return AlchemyJsonResponse( completed=True, - message="DecisionBook are listed successfully", + message="Events are listed successfully", result=records, ) return AlchemyJsonResponse( completed=False, - message="DecisionBook are NOT listed successfully", + message="Events are NOT listed successfully", result=[], ) diff --git a/api_services/redis/auth_actions/token.py b/api_services/redis/auth_actions/token.py index 66d163a..ba4759d 100644 --- a/api_services/redis/auth_actions/token.py +++ b/api_services/redis/auth_actions/token.py @@ -15,8 +15,8 @@ def parse_token_object_to_dict(request): # from requests import Request or str(endpoint_name) in Config.NOT_SECURE_PATHS ): return valid_token - if 'update' in endpoint_name: - endpoint_name = endpoint_name.split('update')[0] + "update" + if "update" in endpoint_name: + endpoint_name = endpoint_name.split("update")[0] + "update" endpoint_active = EndpointRestriction.filter_one( EndpointRestriction.endpoint_name.ilike(f"%{endpoint_name}%"), system=True, diff --git a/api_validations/validations_request/building.py b/api_validations/validations_request/building.py index 4a1ad07..4b344a6 100644 --- a/api_validations/validations_request/building.py +++ b/api_validations/validations_request/building.py @@ -1,4 +1,5 @@ from typing import Optional +from datetime import datetime from api_validations.core_validations import BaseModelRegular from api_validations.validations_request import ( PydanticBaseModel, @@ -59,8 +60,8 @@ class InsertBuild(BaseModelRegular, BuildValidation): max_floor: int underground_floor: int address_uu_id: str - build_date: str - decision_period_date: str + build_date: datetime + decision_period_date: datetime tax_no: Optional[str] = None lift_count: Optional[int] = None @@ -90,7 +91,7 @@ class UpdateBuild(PydanticBaseModel, BuildUpdateValidation): build_types: Optional[str] = None max_floor: Optional[int] = None underground_floor: Optional[int] = None - build_date: Optional[str] = None + build_date: Optional[datetime] = None tax_no: Optional[str] = None lift_count: Optional[int] = None heating_system: Optional[bool] = None diff --git a/api_validations/validations_request/core_request_validations.py b/api_validations/validations_request/core_request_validations.py index 1dfa452..37081a4 100644 --- a/api_validations/validations_request/core_request_validations.py +++ b/api_validations/validations_request/core_request_validations.py @@ -27,8 +27,8 @@ class ListOptions(BaseModelRegular, ListOptionsValidation): size: Optional[int] = 10 order_field: Optional[str] = "id" order_type: Optional[str] = "asc" - include_joins: Optional[list] = [] - query: Optional[dict] = {} + include_joins: Optional[list] = None + query: Optional[dict] = None class CrudRecordValidation: diff --git a/api_validations/validations_request/events.py b/api_validations/validations_request/events.py index c19d9f3..d1a6b28 100644 --- a/api_validations/validations_request/events.py +++ b/api_validations/validations_request/events.py @@ -18,7 +18,7 @@ class RegisterEvents2EmployeeValidation: class RegisterEvents2Employee(BaseModelRegular, RegisterEvents2EmployeeValidation): - event_uu_id_list: list[str] = [] + event_uu_id_list: list[str] = None employee_uu_id: Optional[str] = None @@ -36,6 +36,6 @@ class RegisterEvents2OccupantValidation: class RegisterEvents2Occupant(BaseModelRegular, RegisterEvents2OccupantValidation): - event_uu_id_list: list[str] = [] + event_uu_id_list: list[str] = None build_part_uu_id: Optional[str] = None occupant_uu_id: Optional[str] = None diff --git a/databases/sql_models/building/build.py b/databases/sql_models/building/build.py index 376a091..0fc53ba 100644 --- a/databases/sql_models/building/build.py +++ b/databases/sql_models/building/build.py @@ -196,7 +196,6 @@ class Build(CrudCollection, SelectActionWithEmployee): foreign_keys="BuildDecisionBook.build_id", ) - # # build_ibans: Mapped["BuildIbans"] = relationship( # "BuildIbans", back_populates="building", foreign_keys="BuildIbans.build_id" # ) @@ -271,21 +270,20 @@ class Build(CrudCollection, SelectActionWithEmployee): @classmethod def update_action(cls, data: UpdateBuild, build_uu_id: str, token): from databases import Addresses - print('data_dict', data.dump()) + + print("data_dict", data.dump()) data_dict = data.excluded_dump() if data.address_uu_id: official_address = Addresses.filter_one( Addresses.uu_id == data.address_uu_id ).data data_dict["address_id"] = official_address.id if official_address else None - print('data_dict', data_dict) - if build_to_update := cls.filter_one( - cls.uu_id == build_uu_id - ).data: - print('build_to_update', build_to_update.get_dict()) + print("data_dict", data_dict) + if build_to_update := cls.filter_one(cls.uu_id == build_uu_id).data: + print("build_to_update", build_to_update.get_dict()) updated_build = build_to_update.update(**data_dict) updated_build.save() - print('updated_build', updated_build.get_dict()) + print("updated_build", updated_build.get_dict()) return updated_build @property diff --git a/databases/sql_models/identity/identity.py b/databases/sql_models/identity/identity.py index 42f8e71..44e9ec9 100644 --- a/databases/sql_models/identity/identity.py +++ b/databases/sql_models/identity/identity.py @@ -345,7 +345,9 @@ class People(CrudCollection, SelectAction): @property def full_name(self): - return f"{self.firstname} {self.middle_name} {self.surname}" + if self.middle_name: + return f"{self.firstname} {self.middle_name} {self.surname}" + return f"{self.firstname} {self.surname}" @classmethod def create_action(cls, data: InsertPerson, token): diff --git a/service_app/routers/authentication/router.py b/service_app/routers/authentication/router.py index 3d19f1d..163b977 100644 --- a/service_app/routers/authentication/router.py +++ b/service_app/routers/authentication/router.py @@ -126,8 +126,8 @@ def authentication_forgot_password(request: Request, data: Forgot): @login_route.post(path="/avatar", summary="Get link of avatar with credentials") -def authentication_download_avatar(request: Request, data: Forgot): +def authentication_download_avatar(request: Request): token_dict = parse_token_object_to_dict(request=request) return AuthenticationDownloadAvatarEventMethod.authentication_download_avatar( - data=data, request=request, token_dict=token_dict + token_dict=token_dict ) diff --git a/service_app/routers/validations/router.py b/service_app/routers/validations/router.py index cbffba0..cee20e6 100644 --- a/service_app/routers/validations/router.py +++ b/service_app/routers/validations/router.py @@ -18,6 +18,44 @@ class EndpointValidationResponse(BaseModel): 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) @@ -77,9 +115,9 @@ def user_list(request: Request, validation: EndpointValidation): headers = getattr( active_validation, str(valid_token.lang).lower(), active_validation.tr ) - print("headers", headers) + validation_parse = ValidationParser(active_validation=active_validation) return EndpointValidationResponse( language=valid_token.lang, headers=headers, - validation=active_validation.model_json_schema(), + validation=validation_parse.schema, ) diff --git a/service_app_banks/isbank/isbank_sender.py b/service_app_banks/isbank/isbank_sender.py index c4c3bbf..7113a09 100644 --- a/service_app_banks/isbank/isbank_sender.py +++ b/service_app_banks/isbank/isbank_sender.py @@ -78,8 +78,15 @@ def is_bank_retrieve_account_records(bank_data): data_dict["bank_date_d"] = bank_date.day data_dict["bank_date_y"] = bank_date.year data_dict["bank_date"] = str(bank_date) - if build_iban := BuildIbans.filter_by_one(iban=data_dict["iban"], system=True).data: - data_dict.update({"build_id": build_iban.build_id, "build_uu_id": build_iban.build_uu_id}) + if build_iban := BuildIbans.filter_by_one( + iban=data_dict["iban"], system=True + ).data: + data_dict.update( + { + "build_id": build_iban.build_id, + "build_uu_id": build_iban.build_uu_id, + } + ) if found_record := AccountRecords.filter_one( AccountRecords.bank_date == data_dict["bank_date"], AccountRecords.iban == data_dict["iban"],