diff --git a/ApiControllers/abstracts/event_clusters.py b/ApiControllers/abstracts/event_clusters.py index 68af4d4..b7b4e0b 100644 --- a/ApiControllers/abstracts/event_clusters.py +++ b/ApiControllers/abstracts/event_clusters.py @@ -6,6 +6,8 @@ class EventCluster: """ EventCluster """ + def __repr__(self): + return f"EventCluster(name={self.name})" def __init__(self, endpoint_uu_id: str, name: str): self.endpoint_uu_id = endpoint_uu_id @@ -98,10 +100,12 @@ class RouterCluster: RouterCluster """ - event_clusters: dict[str, EventCluster] = {} + def __repr__(self): + return f"RouterCluster(name={self.name})" def __init__(self, name: str): self.name = name + self.event_clusters: dict[str, EventCluster] = {} def set_event_cluster(self, event_cluster: EventCluster): """ diff --git a/ApiDefaults/create_app.py b/ApiDefaults/create_app.py index 1b3159a..5f4654b 100644 --- a/ApiDefaults/create_app.py +++ b/ApiDefaults/create_app.py @@ -3,11 +3,12 @@ from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse +cluster_is_set = False def create_events_if_any_cluster_set(): import Events - - if not Events.__all__: + global cluster_is_set + if not Events.__all__ or cluster_is_set: return router_cluster_stack: list[RouterCluster] = [ @@ -18,12 +19,13 @@ def create_events_if_any_cluster_set(): router_cluster.event_clusters.values() ) for event_cluster in event_cluster_stack: - print(f"Creating event:", event_cluster.name) try: event_cluster.set_events_to_database() except Exception as e: print(f"Error creating event cluster: {e}") + cluster_is_set = True + def create_app(): from ApiDefaults.open_api_creator import create_openapi_schema @@ -58,6 +60,7 @@ def create_app(): route_register = RouteRegisterController(app=application, router_list=get_routes()) application = route_register.register_routes() + create_events_if_any_cluster_set() application.openapi = lambda _=application: create_openapi_schema(_) return application diff --git a/ApiServices/IdentityService/Endpoints/user/route.py b/ApiServices/IdentityService/Endpoints/user/route.py index 76aa906..a9bba73 100644 --- a/ApiServices/IdentityService/Endpoints/user/route.py +++ b/ApiServices/IdentityService/Endpoints/user/route.py @@ -1,11 +1,13 @@ import uuid -from fastapi import APIRouter, Request, Response, Header +from fastapi import APIRouter, Header +from typing import Any from ApiDefaults.config import api_config from Events.user.cluster import UserRouterCluster from ApiControllers.providers.token_provider import TokenProvider - +from ApiControllers.abstracts.default_validations import CommonHeaders +from Controllers.Postgres.pagination import PaginateOnly user_route = APIRouter(prefix="/user", tags=["User"]) @@ -13,103 +15,58 @@ user_route = APIRouter(prefix="/user", tags=["User"]) @user_route.post( path="/list", description="List users endpoint", - operation_id="5bc09312-d3f2-4f47-baba-17c928706da8", + operation_id="1aca3094-fe80-4e0f-a460-1a506419082a", ) def user_list_route( - request: Request, - response: Response, - language: str = Header(None, alias="language"), - domain: str = Header(None, alias="domain"), - tz: str = Header(None, alias="timezone"), + data: PaginateOnly, + headers: CommonHeaders, ): """ List users endpoint """ - endpoint_code = "5bc09312-d3f2-4f47-baba-17c928706da8" - token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None) - token_object = TokenProvider.get_dict_from_redis(token=token) - event_key = TokenProvider.retrieve_event_codes( - endpoint_code=endpoint_code, token=token_object - ) - headers = { - "language": language or "", - "domain": domain or "", - "eys-ext": f"{str(uuid.uuid4())}", - "tz": tz or "GMT+3", - "token": token, - } - event_cluster_matched = UserRouterCluster.get_event_cluster("UserList").match_event( - event_key=event_key - ) - response.headers["X-Header"] = "Test Header GET" - if runner_callable := event_cluster_matched.event_callable(): - return runner_callable - raise ValueError("Event key not found or multiple matches found") + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = UserRouterCluster.get_event_cluster("UserList") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) @user_route.post( path="/create", description="Create users endpoint", - operation_id="08d4b572-1584-47bb-aa42-8d068e5514e7", + operation_id="9686211f-4260-485d-8076-186c22c053aa", ) def user_create_route( - request: Request, - response: Response, - language: str = Header(None, alias="language"), - domain: str = Header(None, alias="domain"), - tz: str = Header(None, alias="timezone"), + data: Any, + headers: CommonHeaders, ): """ Create users endpoint """ - endpoint_code = "08d4b572-1584-47bb-aa42-8d068e5514e7" - token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None) - token_object = TokenProvider.get_dict_from_redis(token=token) - event_key = TokenProvider.retrieve_event_codes( - endpoint_code=endpoint_code, token=token_object - ) - headers = { - "language": language or "", - "domain": domain or "", - "eys-ext": f"{str(uuid.uuid4())}", - "tz": tz or "GMT+3", - "token": token, - } - event_cluster_matched = UserRouterCluster.get_event_cluster( - "UserCreate" - ).match_event(event_key=event_key) - response.headers["X-Header"] = "Test Header POST" - if runner_callable := event_cluster_matched.event_callable(): - return runner_callable - raise ValueError("Event key not found or multiple matches found") + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = UserRouterCluster.get_event_cluster("UserCreate") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) @user_route.post( path="/update", description="Update users endpoint", - operation_id="b641236a-928d-4f19-a1d2-5edf611d1e56", + operation_id="268e887b-5ff5-4f99-b1be-7e127e28a198", ) -def user_update_route(request: Request, response: Response): +def user_update_route( + data: Any, + headers: CommonHeaders, +): """ Update users endpoint """ - endpoint_code = "b641236a-928d-4f19-a1d2-5edf611d1e56" - token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None) - token_object = TokenProvider.get_dict_from_redis(token=token) - event_key = TokenProvider.retrieve_event_codes( - endpoint_code=endpoint_code, token=token_object - ) - headers = { - "language": language or "", - "domain": domain or "", - "eys-ext": f"{str(uuid.uuid4())}", - "tz": tz or "GMT+3", - "token": token, - } - event_cluster_matched = UserRouterCluster.get_event_cluster( - "UserUpdate" - ).match_event(event_key=event_key) - response.headers["X-Header"] = "Test Header POST" - if runner_callable := event_cluster_matched.event_callable(): - return runner_callable - raise ValueError("Event key not found or multiple matches found") + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = UserRouterCluster.get_event_cluster("UserUpdate") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) \ No newline at end of file diff --git a/ApiServices/IdentityService/Events/user/cluster.py b/ApiServices/IdentityService/Events/user/cluster.py index 0ad56c0..58b602b 100644 --- a/ApiServices/IdentityService/Events/user/cluster.py +++ b/ApiServices/IdentityService/Events/user/cluster.py @@ -8,17 +8,17 @@ from .supers_events import ( UserRouterCluster = RouterCluster(name="UserRouterCluster") UserEventClusterList = EventCluster( - name="UserList", endpoint_uu_id="5bc09312-d3f2-4f47-baba-17c928706da8" + name="UserList", endpoint_uu_id="1aca3094-fe80-4e0f-a460-1a506419082a" ) UserEventClusterList.add_event(SuperUsersListEvent) UserEventClusterCreate = EventCluster( - name="UserCreate", endpoint_uu_id="08d4b572-1584-47bb-aa42-8d068e5514e7" + name="UserCreate", endpoint_uu_id="9686211f-4260-485d-8076-186c22c053aa" ) UserEventClusterCreate.add_event(SuperUsersCreateEvent) UserEventClusterUpdate = EventCluster( - name="UserUpdate", endpoint_uu_id="b641236a-928d-4f19-a1d2-5edf611d1e56" + name="UserUpdate", endpoint_uu_id="268e887b-5ff5-4f99-b1be-7e127e28a198" ) UserEventClusterUpdate.add_event(SuperUsersUpdateEvent) diff --git a/ApiServices/ManagementService/Endpoints/application/route.py b/ApiServices/ManagementService/Endpoints/application/route.py index 3e4a7f3..54f4d7f 100644 --- a/ApiServices/ManagementService/Endpoints/application/route.py +++ b/ApiServices/ManagementService/Endpoints/application/route.py @@ -1,4 +1,5 @@ from fastapi import APIRouter, Depends +from typing import Any from ApiControllers.abstracts.default_validations import CommonHeaders from ApiControllers.providers.token_provider import TokenProvider @@ -17,7 +18,7 @@ application_route = APIRouter(prefix="/application", tags=["Application Manageme @application_route.post( path="/list", description="List applications endpoint", - operation_id="application-list", + operation_id="3189a049-bdb0-49f3-83ff-feb8cb4cb57a", ) def application_list_route( list_options: PaginateOnly, @@ -50,7 +51,7 @@ def application_list_route( @application_route.post( path="/create", description="Create application endpoint", - operation_id="application-create", + operation_id="5570be78-030a-438e-8674-7e751447608b", ) def application_create_route( data: RequestApplication, @@ -85,7 +86,7 @@ def application_create_route( @application_route.post( path="/update/{application_uuid}", description="Update application endpoint", - operation_id="application-update", + operation_id="87cd4515-73dd-4d11-a01f-562e221d973c", ) def application_update_route( data: RequestApplication, @@ -119,36 +120,42 @@ def application_update_route( ).response -@application_route.delete( - path="/{application_uuid}", - description="Delete application endpoint", - operation_id="application-delete", +@application_route.post( + path="/bind/employee", + description="Bind application to employee endpoint", + operation_id="2bab94fa-becb-4d8e-80f1-f4631119a521", ) -def application_delete_route( - application_uuid: str, - headers: CommonHeaders = Depends(CommonHeaders.as_dependency), +def application_bind_employee_route( + data: Any, + headers: CommonHeaders, ): """ - Delete application by ID + Bind application to employee endpoint """ token_object = TokenProvider.get_dict_from_redis(token=headers.token) - with Applications.new_session() as db_session: - found_application = Applications.filter_one( - Applications.uu_id == application_uuid, db=db_session - ).data - if not found_application: - return EndpointResponse( - message="MSG0002-FOUND", - data=found_application, - ).response - found_application.destroy(db_session) - Applications.save(db_session) - if found_application.meta_data.deleted: - return EndpointResponse( - message="MSG0004-DELETE", - data=found_application, - ).response - return EndpointResponse( - message="MSG0004-DELETE", - data=found_application, - ).response + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationBindEmployee") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) + + +@application_route.post( + path="/bind/occupant", + description="Bind application to occupant endpoint", + operation_id="fccf1a59-0650-4e5c-ba8d-f389dadce01c", +) +def application_bind_occupant_route( + data: Any, + headers: CommonHeaders, +): + """ + Bind application to occupant endpoint + """ + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = ApplicationRouterCluster.get_event_cluster("ApplicationBindOccupant") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) + \ No newline at end of file diff --git a/ApiServices/ManagementService/Endpoints/event_endpoints/route.py b/ApiServices/ManagementService/Endpoints/event_endpoints/route.py index 38e15ce..9877e45 100644 --- a/ApiServices/ManagementService/Endpoints/event_endpoints/route.py +++ b/ApiServices/ManagementService/Endpoints/event_endpoints/route.py @@ -1,8 +1,8 @@ from fastapi import APIRouter, Depends +from typing import Any from ApiControllers.abstracts.default_validations import CommonHeaders from ApiControllers.providers.token_provider import TokenProvider - from Controllers.Postgres.pagination import PaginateOnly, Pagination, PaginationResult from Controllers.Postgres.response import EndpointResponse from Validations.service_endpoints.validations import Event2Employee, Event2Occupant @@ -26,46 +26,66 @@ def event_list_route( token_object = TokenProvider.get_dict_from_redis(token=headers.token) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) - FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventList") + FoundCluster = EventsEndpointRouterCluster.get_event_cluster("EventsList") event_cluster_matched = FoundCluster.match_event(event_key=event_key) return event_cluster_matched.event_callable(data=data) @event_endpoint_route.post( - path="/bind/employee", - description="Bind event to employee endpoint", - operation_id="", + path="/register/service", + description="Register event to service endpoint", + operation_id="c89a2150-db4d-4a8f-b6ec-9e0f09625f76", ) -def event_bind_employee_route( - data: Event2Employee, +def event_register_service_route( + data: Any, headers: CommonHeaders = Depends(CommonHeaders.as_dependency), ): """ - Bind event to employee + Register event to service """ token_object = TokenProvider.get_dict_from_redis(token=headers.token) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) - FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventBindEmployee") + FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventRegisterService") event_cluster_matched = FoundCluster.match_event(event_key=event_key) return event_cluster_matched.event_callable(data=data) @event_endpoint_route.post( - path="/bind/occupant", - description="Bind event to occupant endpoint", - operation_id="", + path="/bind/extra/employee", + description="Bind event to employee extra endpoint", + operation_id="58ef3640-04ec-43f9-8f3e-f86be3ce4a24", ) -def event_bind_occupant_route( - data: Event2Occupant, +def event_bind_employee_extra_route( + data: Any, headers: CommonHeaders = Depends(CommonHeaders.as_dependency), ): """ - Bind event to occupant + Bind event to employee extra """ token_object = TokenProvider.get_dict_from_redis(token=headers.token) event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) - FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventBindOccupant") + FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventBindEmployeeExtra") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) + + +@event_endpoint_route.post( + path="/bind/extra/occupant", + description="Bind event to occupant extra endpoint", + operation_id="7794a550-3073-43e3-b0c5-80128f8d3e4b", +) +def event_bind_occupant_extra_route( + data: Any, + headers: CommonHeaders = Depends(CommonHeaders.as_dependency), +): + """ + Bind event to occupant extra + """ + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = EventEndpointRouterCluster.get_event_cluster("EventBindOccupantExtra") event_cluster_matched = FoundCluster.match_event(event_key=event_key) return event_cluster_matched.event_callable(data=data) diff --git a/ApiServices/ManagementService/Endpoints/routes.py b/ApiServices/ManagementService/Endpoints/routes.py index f9cd2d0..8ed511a 100644 --- a/ApiServices/ManagementService/Endpoints/routes.py +++ b/ApiServices/ManagementService/Endpoints/routes.py @@ -3,8 +3,11 @@ from fastapi import APIRouter def get_routes() -> list[APIRouter]: from .application.route import application_route + from .service_endpoints.route import service_endpoint_route + from .service_managements.route import service_management_route + from .event_endpoints.route import event_endpoint_route - return [application_route] + return [application_route, service_endpoint_route, service_management_route, event_endpoint_route] def get_safe_endpoint_urls() -> list[tuple[str, str]]: diff --git a/ApiServices/ManagementService/Events/__init__.py b/ApiServices/ManagementService/Events/__init__.py index a9a2c5b..dec62dd 100644 --- a/ApiServices/ManagementService/Events/__init__.py +++ b/ApiServices/ManagementService/Events/__init__.py @@ -1 +1,5 @@ -__all__ = [] +from .service_endpoints.cluster import ServiceEndpointRouterCluster +from .event_endpoints.cluster import EventsEndpointRouterCluster +from .application.cluster import ApplicationRouterCluster + +__all__ = ["ServiceEndpointRouterCluster", "EventsEndpointRouterCluster", "ApplicationRouterCluster"] diff --git a/ApiServices/ManagementService/Events/application/cluster.py b/ApiServices/ManagementService/Events/application/cluster.py index 0ad56c0..f247ea7 100644 --- a/ApiServices/ManagementService/Events/application/cluster.py +++ b/ApiServices/ManagementService/Events/application/cluster.py @@ -1,27 +1,41 @@ from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster from .supers_events import ( - SuperUsersListEvent, - SuperUsersCreateEvent, - SuperUsersUpdateEvent, + ApplicationListEvent, + ApplicationCreateEvent, + ApplicationUpdateEvent, + ApplicationBindEmployeeEvent, + ApplicationBindOccupantEvent, ) -UserRouterCluster = RouterCluster(name="UserRouterCluster") +ApplicationRouterCluster = RouterCluster(name="ApplicationRouterCluster") -UserEventClusterList = EventCluster( - name="UserList", endpoint_uu_id="5bc09312-d3f2-4f47-baba-17c928706da8" +ApplicationEventClusterList = EventCluster( + name="ApplicationList", endpoint_uu_id="3189a049-bdb0-49f3-83ff-feb8cb4cb57a" ) -UserEventClusterList.add_event(SuperUsersListEvent) +ApplicationEventClusterList.add_event(ApplicationListEvent) -UserEventClusterCreate = EventCluster( - name="UserCreate", endpoint_uu_id="08d4b572-1584-47bb-aa42-8d068e5514e7" +ApplicationEventClusterCreate = EventCluster( + name="ApplicationCreate", endpoint_uu_id="5570be78-030a-438e-8674-7e751447608b" ) -UserEventClusterCreate.add_event(SuperUsersCreateEvent) +ApplicationEventClusterCreate.add_event(ApplicationCreateEvent) -UserEventClusterUpdate = EventCluster( - name="UserUpdate", endpoint_uu_id="b641236a-928d-4f19-a1d2-5edf611d1e56" +ApplicationEventClusterUpdate = EventCluster( + name="ApplicationUpdate", endpoint_uu_id="87cd4515-73dd-4d11-a01f-562e221d973c" ) -UserEventClusterUpdate.add_event(SuperUsersUpdateEvent) +ApplicationEventClusterUpdate.add_event(ApplicationUpdateEvent) -UserRouterCluster.set_event_cluster(UserEventClusterList) -UserRouterCluster.set_event_cluster(UserEventClusterCreate) -UserRouterCluster.set_event_cluster(UserEventClusterUpdate) +ApplicationEventClusterBindEmployee = EventCluster( + name="ApplicationBindEmployee", endpoint_uu_id="2bab94fa-becb-4d8e-80f1-f4631119a521" +) +ApplicationEventClusterBindEmployee.add_event(ApplicationBindEmployeeEvent) + +ApplicationEventClusterBindOccupant = EventCluster( + name="ApplicationBindOccupant", endpoint_uu_id="fccf1a59-0650-4e5c-ba8d-f389dadce01c" +) +ApplicationEventClusterBindOccupant.add_event(ApplicationBindOccupantEvent) + +ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterList) +ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterCreate) +ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterUpdate) +ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterBindEmployee) +ApplicationRouterCluster.set_event_cluster(ApplicationEventClusterBindOccupant) diff --git a/ApiServices/ManagementService/Events/application/supers_events.py b/ApiServices/ManagementService/Events/application/supers_events.py index 679e2a6..21a8633 100644 --- a/ApiServices/ManagementService/Events/application/supers_events.py +++ b/ApiServices/ManagementService/Events/application/supers_events.py @@ -1,53 +1,75 @@ from ApiControllers.abstracts.event_clusters import Event from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly from Controllers.Postgres.response import EndpointResponse -from Schemas import Users +from Schemas import ( + Applications, + Application2Employee, + Application2Occupant, +) # List endpoint -SuperUsersListEvent = Event( - name="supers_users_list", - key="341b394f-9f11-4abb-99e7-4b27fa6bf012", +ApplicationListEvent = Event( + name="application_list", + key="b4efda1e-bde7-4659-ab1a-ef74c0fd88b6", request_validator=None, # TODO: Add request validator response_validator=None, # TODO: Add response validator description="List events of users endpoint", ) # Create endpoint -SuperUsersCreateEvent = Event( - name="supers_users_create", - key="4e7e189e-e015-4ff8-902d-60138cbc77a6", +ApplicationCreateEvent = Event( + name="application_create", + key="f53ca9aa-5536-4d77-9129-78d67e61db4a", request_validator=None, # TODO: Add request validator response_validator=None, # TODO: Add response validator description="Create events of users endpoint", ) # Update endpoint -SuperUsersUpdateEvent = Event( - name="supers_users_update", - key="efa4aa4a-d414-4391-91ee-97eb617b7755", +ApplicationUpdateEvent = Event( + name="application_update", + key="0e9a855e-4e69-44b5-8ac2-825daa32840c", request_validator=None, # TODO: Add request validator response_validator=None, # TODO: Add response validator description="Update events of users endpoint", ) + #Bind Application to employee +ApplicationBindEmployeeEvent = Event( + name="application_bind_employee", + key="26a96c2d-bca8-41cb-8ac1-f3ca8124434b", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Bind events of users endpoint", +) -def supers_users_list_callable(list_options: PaginateOnly): +#Bind Application to occupant +ApplicationBindOccupantEvent = Event( + name="application_bind_occupant", + key="4eaf2bb0-2a42-4d21-ae65-a9259ebee189", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Bind events of users endpoint", +) + + +def application_list_callable(list_options: PaginateOnly): """ Example callable method """ list_options = PaginateOnly(**list_options.model_dump()) - with Users.new_session() as db_session: + with Applications.new_session() as db_session: if list_options.query: - users_list = Users.filter_all( - *Users.convert(list_options.query), db=db_session + applications_list = Applications.filter_all( + *Applications.convert(list_options.query), db=db_session ) else: - users_list = Users.filter_all(db=db_session) - pagination = Pagination(data=users_list) + applications_list = Applications.filter_all(db=db_session) + pagination = Pagination(data=applications_list) pagination.change(**list_options.model_dump()) pagination_result = PaginationResult( - data=users_list, + data=applications_list, pagination=pagination, # response_model="", ).pagination.as_dict @@ -57,10 +79,10 @@ def supers_users_list_callable(list_options: PaginateOnly): ).response -SuperUsersListEvent.event_callable = supers_users_list_callable +ApplicationListEvent.event_callable = application_list_callable -def supers_users_create_callable(): +def application_create_callable(): """ Example callable method """ @@ -74,10 +96,10 @@ def supers_users_create_callable(): } -SuperUsersCreateEvent.event_callable = supers_users_create_callable +ApplicationCreateEvent.event_callable = application_create_callable -def supers_users_update_callable(): +def application_update_callable(): """ Example callable method """ @@ -91,4 +113,36 @@ def supers_users_update_callable(): } -SuperUsersUpdateEvent.event_callable = supers_users_update_callable +ApplicationUpdateEvent.event_callable = application_update_callable + + +def application_bind_employee_callable(): + """ + Example callable method + """ + return { + "completed": True, + "message": "Example callable method 2", + "info": { + "host": "example_host", + "user_agent": "example_user_agent", + }, + } + +ApplicationBindEmployeeEvent.event_callable = application_bind_employee_callable + + +def application_bind_occupant_callable(): + """ + Example callable method + """ + return { + "completed": True, + "message": "Example callable method 2", + "info": { + "host": "example_host", + "user_agent": "example_user_agent", + }, + } + +ApplicationBindOccupantEvent.event_callable = application_bind_occupant_callable diff --git a/ApiServices/ManagementService/Events/event_endpoints/cluster.py b/ApiServices/ManagementService/Events/event_endpoints/cluster.py new file mode 100644 index 0000000..b025ad1 --- /dev/null +++ b/ApiServices/ManagementService/Events/event_endpoints/cluster.py @@ -0,0 +1,35 @@ +from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster +from .supers_events import ( + EventsListEvent, + EventRegisterServiceEvent, + EventBindEmployeeExtraEvent, + EventBindOccupantExtraEvent, +) + +EventsEndpointRouterCluster = RouterCluster(name="EventsEndpointRouterCluster") + +EventsEndpointEventClusterList = EventCluster( + name="EventsList", endpoint_uu_id="0659d5e4-671f-466c-a84f-47a1290a6f0d" +) +EventsEndpointEventClusterList.add_event(EventsListEvent) + +EventsEndpointEventClusterRegisterService = EventCluster( + name="EventRegisterService", endpoint_uu_id="c89a2150-db4d-4a8f-b6ec-9e0f09625f76" +) +EventsEndpointEventClusterRegisterService.add_event(EventRegisterServiceEvent) + +EventsEndpointEventClusterBindEmployeeExtra = EventCluster( + name="EventBindEmployeeExtra", endpoint_uu_id="58ef3640-04ec-43f9-8f3e-f86be3ce4a24" +) +EventsEndpointEventClusterBindEmployeeExtra.add_event(EventBindEmployeeExtraEvent) + +EventsEndpointEventClusterBindOccupantExtra = EventCluster( + name="EventBindOccupantExtra", endpoint_uu_id="7794a550-3073-43e3-b0c5-80128f8d3e4b" +) +EventsEndpointEventClusterBindOccupantExtra.add_event(EventBindOccupantExtraEvent) + +EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterList) +EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterRegisterService) +EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterBindEmployeeExtra) +EventsEndpointRouterCluster.set_event_cluster(EventsEndpointEventClusterBindOccupantExtra) + \ No newline at end of file diff --git a/ApiServices/ManagementService/Events/event_endpoints/supers_events.py b/ApiServices/ManagementService/Events/event_endpoints/supers_events.py new file mode 100644 index 0000000..6a1cb4c --- /dev/null +++ b/ApiServices/ManagementService/Events/event_endpoints/supers_events.py @@ -0,0 +1,107 @@ +from ApiControllers.abstracts.event_clusters import Event +from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly +from Controllers.Postgres.response import EndpointResponse +from typing import Any +from Schemas import ( + Events, + Event2Employee, + Event2Occupant, + Event2EmployeeExtra, + Event2OccupantExtra, + Service2Events, +) + + +# List endpoint +EventsListEvent = Event( + name="service_endpoint_list", + key="0a08c64b-ce20-4791-b1e9-014db6b75ea7", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="List services endpoint", +) + + +# Event Register endpoint +EventRegisterServiceEvent = Event( + name="service_endpoint_register_service", + key="e18e7f89-5708-4a15-9258-99b0903ed43d", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Register service endpoint", +) + +# Bind employee extra endpoint +EventBindEmployeeExtraEvent = Event( + name="service_endpoint_bind_employee_extra", + key="cd452928-4256-4fb4-b81e-0ca41d723616", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Bind service to employee extra endpoint", +) + +# Bind occupant extra endpoint +EventBindOccupantExtraEvent = Event( + name="service_endpoint_bind_occupant_extra", + key="cb11a150-8049-45c9-8cf3-d5290ffd2e4a", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Bind service to occupant extra endpoint", +) + + +def events_list_callable(list_options: PaginateOnly): + """ + Example callable method + """ + list_options = PaginateOnly(**list_options.model_dump()) + with Services.new_session() as db_session: + if list_options.query: + services_list = Services.filter_all( + *Services.convert(list_options.query), db=db_session + ) + else: + services_list = Services.filter_all(db=db_session) + pagination = Pagination(data=services_list) + pagination.change(**list_options.model_dump()) + pagination_result = PaginationResult( + data=services_list, + pagination=pagination, + # response_model="", + ).pagination.as_dict + return EndpointResponse( + message="MSG0003-LIST", + pagination_result=pagination_result, + ).response + +EventsListEvent.event_callable = events_list_callable + +def event_register_service_callable(data: Any): + """ + Example callable method + """ + return EndpointResponse( + message="MSG0003-REGISTER", + ).response + +EventRegisterServiceEvent.event_callable = event_register_service_callable + +def event_bind_employee_extra_callable(data: Any): + """ + Example callable method + """ + return EndpointResponse( + message="MSG0003-BIND", + ).response + +EventBindEmployeeExtraEvent.event_callable = event_bind_employee_extra_callable + +def event_bind_occupant_extra_callable(data: Any): + """ + Example callable method + """ + return EndpointResponse( + message="MSG0003-BIND", + ).response + +EventBindOccupantExtraEvent.event_callable = event_bind_occupant_extra_callable diff --git a/ApiServices/ManagementService/Events/service_endpoints/cluster.py b/ApiServices/ManagementService/Events/service_endpoints/cluster.py index 70b4b7e..2465547 100644 --- a/ApiServices/ManagementService/Events/service_endpoints/cluster.py +++ b/ApiServices/ManagementService/Events/service_endpoints/cluster.py @@ -5,7 +5,7 @@ from .supers_events import ( ServiceEndpointRouterCluster = RouterCluster(name="ServiceEndpointRouterCluster") ServiceEndpointEventClusterList = EventCluster( - name="ServiceList", endpoint_uu_id="82ef3444-a26a-499d-8c77-ca2e95d6ceb9" + name="ServiceList", endpoint_uu_id="f4e4d332-70b1-4121-9fcc-a08850b72aaa" ) ServiceEndpointEventClusterList.add_event(ServiceEndpointListEvent) ServiceEndpointRouterCluster.set_event_cluster(ServiceEndpointEventClusterList) diff --git a/ApiServices/TemplateService/Dockerfile b/ApiServices/TemplateService/Dockerfile index 9a144d2..3627a8f 100644 --- a/ApiServices/TemplateService/Dockerfile +++ b/ApiServices/TemplateService/Dockerfile @@ -3,24 +3,27 @@ FROM python:3.12-slim WORKDIR / # Install system dependencies and Poetry -RUN apt-get update && apt-get install -y --no-install-recommends gcc \ - && rm -rf /var/lib/apt/lists/* && pip install --no-cache-dir poetry +RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/* && pip install --no-cache-dir poetry # Copy Poetry configuration COPY /pyproject.toml ./pyproject.toml # Configure Poetry and install dependencies with optimizations -RUN poetry config virtualenvs.create false \ - && poetry install --no-interaction --no-ansi --no-root --only main \ - && pip cache purge && rm -rf ~/.cache/pypoetry +RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi --no-root --only main \ +&& pip cache purge && rm -rf ~/.cache/pypoetry # Copy application code -COPY /ApiServices/TemplateService /ApiServices/TemplateService +COPY /ApiControllers /ApiControllers +COPY /ApiDefaults /ApiDefaults COPY /Controllers /Controllers COPY /Schemas /Schemas +COPY /ApiServices/IdentityService/Endpoints /ApiDefaults/Endpoints +COPY /ApiServices/IdentityService/Events /ApiDefaults/Events +COPY /ApiServices/IdentityService/Validations /ApiDefaults/Validations + # Set Python path to include app directory ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 # Run the application using the configured uvicorn server -CMD ["poetry", "run", "python", "ApiServices/TemplateService/app.py"] +CMD ["poetry", "run", "python", "ApiDefaults/app.py"] diff --git a/ApiServices/TemplateService/endpoints/__init__.py b/ApiServices/TemplateService/Endpoints/__init__.py similarity index 100% rename from ApiServices/TemplateService/endpoints/__init__.py rename to ApiServices/TemplateService/Endpoints/__init__.py diff --git a/ApiServices/TemplateService/Endpoints/people/route.py b/ApiServices/TemplateService/Endpoints/people/route.py new file mode 100644 index 0000000..8687b2e --- /dev/null +++ b/ApiServices/TemplateService/Endpoints/people/route.py @@ -0,0 +1,67 @@ +from fastapi import APIRouter, Depends + +from ApiControllers.abstracts.default_validations import CommonHeaders +from ApiControllers.providers.token_provider import TokenProvider +from Controllers.Postgres.pagination import PaginateOnly +from Events.people.cluster import PeopleRouterCluster + + +people_route = APIRouter(prefix="/people", tags=["People"]) + + +@people_route.post( + path="/list", + description="List people endpoint", + operation_id="f102db46-031a-43e4-966a-dae6896f985b", +) +def people_route_list( + data: PaginateOnly, + headers: CommonHeaders = Depends(CommonHeaders.as_dependency), +): + """ + List people endpoint + """ + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleList") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) + + +@people_route.post( + path="/create", + description="Create people endpoint", + operation_id="eb465fde-337f-4b81-94cf-28c6d4f2b1b6", +) +def people_route_create( + headers: CommonHeaders = Depends(CommonHeaders.as_dependency), +): + """ + Create people endpoint + """ + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleCreate") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable() + + +@people_route.post( + path="/update", + description="Update people endpoint", + operation_id="c9e5ba69-6915-43f5-8f9c-a5c2aa865b89", +) +def people_route_update( + headers: CommonHeaders = Depends(CommonHeaders.as_dependency), +): + """ + Update people endpoint + """ + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = PeopleRouterCluster.get_event_cluster("PeopleUpdate") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable() diff --git a/ApiServices/TemplateService/Endpoints/routes.py b/ApiServices/TemplateService/Endpoints/routes.py new file mode 100644 index 0000000..df1570b --- /dev/null +++ b/ApiServices/TemplateService/Endpoints/routes.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter + + +def get_routes() -> list[APIRouter]: + from .people.route import people_route + from .user.route import user_route + + return [user_route, people_route] + + +def get_safe_endpoint_urls() -> list[tuple[str, str]]: + return [ + ("/", "GET"), + ("/docs", "GET"), + ("/redoc", "GET"), + ("/openapi.json", "GET"), + ("/metrics", "GET"), + ] diff --git a/ApiServices/TemplateService/Endpoints/user/route.py b/ApiServices/TemplateService/Endpoints/user/route.py new file mode 100644 index 0000000..a9bba73 --- /dev/null +++ b/ApiServices/TemplateService/Endpoints/user/route.py @@ -0,0 +1,72 @@ +import uuid + +from fastapi import APIRouter, Header +from typing import Any + +from ApiDefaults.config import api_config +from Events.user.cluster import UserRouterCluster +from ApiControllers.providers.token_provider import TokenProvider +from ApiControllers.abstracts.default_validations import CommonHeaders +from Controllers.Postgres.pagination import PaginateOnly + +user_route = APIRouter(prefix="/user", tags=["User"]) + + +@user_route.post( + path="/list", + description="List users endpoint", + operation_id="1aca3094-fe80-4e0f-a460-1a506419082a", +) +def user_list_route( + data: PaginateOnly, + headers: CommonHeaders, +): + """ + List users endpoint + """ + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = UserRouterCluster.get_event_cluster("UserList") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) + + +@user_route.post( + path="/create", + description="Create users endpoint", + operation_id="9686211f-4260-485d-8076-186c22c053aa", +) +def user_create_route( + data: Any, + headers: CommonHeaders, +): + """ + Create users endpoint + """ + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = UserRouterCluster.get_event_cluster("UserCreate") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) + + +@user_route.post( + path="/update", + description="Update users endpoint", + operation_id="268e887b-5ff5-4f99-b1be-7e127e28a198", +) +def user_update_route( + data: Any, + headers: CommonHeaders, +): + """ + Update users endpoint + """ + token_object = TokenProvider.get_dict_from_redis(token=headers.token) + event_founder_dict = dict(endpoint_code=headers.operation_id, token=token_object) + event_key = TokenProvider.retrieve_event_codes(**event_founder_dict) + FoundCluster = UserRouterCluster.get_event_cluster("UserUpdate") + event_cluster_matched = FoundCluster.match_event(event_key=event_key) + return event_cluster_matched.event_callable(data=data) \ No newline at end of file diff --git a/ApiServices/TemplateService/Events/__init__.py b/ApiServices/TemplateService/Events/__init__.py new file mode 100644 index 0000000..20b9623 --- /dev/null +++ b/ApiServices/TemplateService/Events/__init__.py @@ -0,0 +1,8 @@ +from .people.cluster import PeopleRouterCluster +from .user.cluster import UserRouterCluster + + +__all__ = [ + "PeopleRouterCluster", + "UserRouterCluster", +] diff --git a/ApiServices/TemplateService/Events/people/cluster.py b/ApiServices/TemplateService/Events/people/cluster.py new file mode 100644 index 0000000..fbd2519 --- /dev/null +++ b/ApiServices/TemplateService/Events/people/cluster.py @@ -0,0 +1,25 @@ +from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster +from .supers_events import ( + SupersPeopleCreateEvent, + SupersPeopleUpdateEvent, + SupersPeopleListEvent, +) + +PeopleRouterCluster = RouterCluster(name="PeopleRouterCluster") + +PeopleEventClusterList = EventCluster( + name="PeopleList", endpoint_uu_id="f102db46-031a-43e4-966a-dae6896f985b" +) +PeopleEventClusterList.add_event(SupersPeopleListEvent) +PeopleEventClusterCreate = EventCluster( + name="PeopleCreate", endpoint_uu_id="eb465fde-337f-4b81-94cf-28c6d4f2b1b6" +) +PeopleEventClusterCreate.add_event(SupersPeopleCreateEvent) +PeopleEventClusterUpdate = EventCluster( + name="PeopleUpdate", endpoint_uu_id="c9e5ba69-6915-43f5-8f9c-a5c2aa865b89" +) +PeopleEventClusterUpdate.add_event(SupersPeopleUpdateEvent) + +PeopleRouterCluster.set_event_cluster(PeopleEventClusterList) +PeopleRouterCluster.set_event_cluster(PeopleEventClusterCreate) +PeopleRouterCluster.set_event_cluster(PeopleEventClusterUpdate) diff --git a/ApiServices/TemplateService/Events/people/supers_events.py b/ApiServices/TemplateService/Events/people/supers_events.py new file mode 100644 index 0000000..b372619 --- /dev/null +++ b/ApiServices/TemplateService/Events/people/supers_events.py @@ -0,0 +1,103 @@ +from ApiControllers.abstracts.event_clusters import Event +from Validations.people.validations import ( + REQUESTAWMXNTKMGPPOJWRCTZUBADNFLQDBDYVQAORFAVCSXUUHEBQHCEPCSKFBADBODFDBPYKOVINV, +) +from Controllers.Postgres.pagination import ( + ListOptions, + Pagination, + PaginationResult, + PaginateOnly, +) +from Controllers.Postgres.response import EndpointResponse +from Schemas import People + + +SupersPeopleCreateEvent = Event( + name="supers_people_create", + key="ec4c2404-a61b-46c7-bbdf-ce3357e6cf41", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Create events of people endpoint", +) + +# Update endpoint +SupersPeopleUpdateEvent = Event( + name="supers_people_update", + key="91e77de4-9f29-4309-b121-4aad256d440c", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Update events of people endpoint", +) + +# List endpoint +SupersPeopleListEvent = Event( + name="supers_people_list", + key="6828d280-e587-400d-a622-c318277386c3", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="List events of people endpoint", +) + + +def supers_people_create_callable(): + """ + Example callable method + """ + + return { + "completed": True, + "message": "Example callable method 2", + "info": { + "host": "example_host", + "user_agent": "example_user_agent", + }, + } + + +SupersPeopleCreateEvent.event_callable = supers_people_create_callable + + +def supers_people_update_callable(): + """ + Example callable method + """ + + return { + "completed": True, + "message": "Example callable method 2", + "info": { + "host": "example_host", + "user_agent": "example_user_agent", + }, + } + + +SupersPeopleUpdateEvent.event_callable = supers_people_update_callable + + +def supers_people_list_callable(data: PaginateOnly): + """ + Example callable method + """ + list_options = PaginateOnly(**data.model_dump()) + with People.new_session() as db_session: + if list_options.query: + people_list = People.filter_all( + *People.convert(list_options.query), db=db_session + ) + else: + people_list = People.filter_all(db=db_session) + pagination = Pagination(data=people_list) + pagination.change(**list_options.model_dump()) + pagination_result = PaginationResult( + data=people_list, + pagination=pagination, + response_model=REQUESTAWMXNTKMGPPOJWRCTZUBADNFLQDBDYVQAORFAVCSXUUHEBQHCEPCSKFBADBODFDBPYKOVINV, + ) + return EndpointResponse( + message="MSG0002-LIST", + pagination_result=pagination_result, + ).response + + +SupersPeopleListEvent.event_callable = supers_people_list_callable diff --git a/ApiServices/TemplateService/Events/user/cluster.py b/ApiServices/TemplateService/Events/user/cluster.py new file mode 100644 index 0000000..58b602b --- /dev/null +++ b/ApiServices/TemplateService/Events/user/cluster.py @@ -0,0 +1,27 @@ +from ApiControllers.abstracts.event_clusters import EventCluster, RouterCluster +from .supers_events import ( + SuperUsersListEvent, + SuperUsersCreateEvent, + SuperUsersUpdateEvent, +) + +UserRouterCluster = RouterCluster(name="UserRouterCluster") + +UserEventClusterList = EventCluster( + name="UserList", endpoint_uu_id="1aca3094-fe80-4e0f-a460-1a506419082a" +) +UserEventClusterList.add_event(SuperUsersListEvent) + +UserEventClusterCreate = EventCluster( + name="UserCreate", endpoint_uu_id="9686211f-4260-485d-8076-186c22c053aa" +) +UserEventClusterCreate.add_event(SuperUsersCreateEvent) + +UserEventClusterUpdate = EventCluster( + name="UserUpdate", endpoint_uu_id="268e887b-5ff5-4f99-b1be-7e127e28a198" +) +UserEventClusterUpdate.add_event(SuperUsersUpdateEvent) + +UserRouterCluster.set_event_cluster(UserEventClusterList) +UserRouterCluster.set_event_cluster(UserEventClusterCreate) +UserRouterCluster.set_event_cluster(UserEventClusterUpdate) diff --git a/ApiServices/TemplateService/Events/user/supers_events.py b/ApiServices/TemplateService/Events/user/supers_events.py new file mode 100644 index 0000000..679e2a6 --- /dev/null +++ b/ApiServices/TemplateService/Events/user/supers_events.py @@ -0,0 +1,94 @@ +from ApiControllers.abstracts.event_clusters import Event +from Controllers.Postgres.pagination import Pagination, PaginationResult, PaginateOnly +from Controllers.Postgres.response import EndpointResponse +from Schemas import Users + + +# List endpoint +SuperUsersListEvent = Event( + name="supers_users_list", + key="341b394f-9f11-4abb-99e7-4b27fa6bf012", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="List events of users endpoint", +) + +# Create endpoint +SuperUsersCreateEvent = Event( + name="supers_users_create", + key="4e7e189e-e015-4ff8-902d-60138cbc77a6", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Create events of users endpoint", +) + +# Update endpoint +SuperUsersUpdateEvent = Event( + name="supers_users_update", + key="efa4aa4a-d414-4391-91ee-97eb617b7755", + request_validator=None, # TODO: Add request validator + response_validator=None, # TODO: Add response validator + description="Update events of users endpoint", +) + + +def supers_users_list_callable(list_options: PaginateOnly): + """ + Example callable method + """ + list_options = PaginateOnly(**list_options.model_dump()) + with Users.new_session() as db_session: + if list_options.query: + users_list = Users.filter_all( + *Users.convert(list_options.query), db=db_session + ) + else: + users_list = Users.filter_all(db=db_session) + pagination = Pagination(data=users_list) + pagination.change(**list_options.model_dump()) + pagination_result = PaginationResult( + data=users_list, + pagination=pagination, + # response_model="", + ).pagination.as_dict + return EndpointResponse( + message="MSG0003-LIST", + pagination_result=pagination_result, + ).response + + +SuperUsersListEvent.event_callable = supers_users_list_callable + + +def supers_users_create_callable(): + """ + Example callable method + """ + return { + "completed": True, + "message": "Example callable method 2", + "info": { + "host": "example_host", + "user_agent": "example_user_agent", + }, + } + + +SuperUsersCreateEvent.event_callable = supers_users_create_callable + + +def supers_users_update_callable(): + """ + Example callable method + """ + return { + "completed": True, + "message": "Example callable method 2", + "info": { + "host": "example_host", + "user_agent": "example_user_agent", + }, + } + + +SuperUsersUpdateEvent.event_callable = supers_users_update_callable diff --git a/ApiServices/TemplateService/exception_handlers/__init__.py b/ApiServices/TemplateService/Validations/__init__.py similarity index 100% rename from ApiServices/TemplateService/exception_handlers/__init__.py rename to ApiServices/TemplateService/Validations/__init__.py diff --git a/ApiServices/TemplateService/Validations/lists/validations.py b/ApiServices/TemplateService/Validations/lists/validations.py new file mode 100644 index 0000000..d3e7125 --- /dev/null +++ b/ApiServices/TemplateService/Validations/lists/validations.py @@ -0,0 +1,26 @@ +from pydantic import BaseModel +from typing import Optional + + +class ListOptions(BaseModel): + """ + Query for list option abilities + """ + + page: Optional[int] = 1 + size: Optional[int] = 10 + order_field: Optional[str] = "id" + order_type: Optional[str] = "asc" + # include_joins: Optional[list] = None + query: Optional[dict] = None + + +class PaginateOnly(BaseModel): + """ + Query for list option abilities + """ + + page: Optional[int] = 1 + size: Optional[int] = 10 + order_field: Optional[str] = "id" + order_type: Optional[str] = "asc" diff --git a/ApiServices/TemplateService/Validations/people/validations.py b/ApiServices/TemplateService/Validations/people/validations.py new file mode 100644 index 0000000..7ccfe77 --- /dev/null +++ b/ApiServices/TemplateService/Validations/people/validations.py @@ -0,0 +1,24 @@ +from pydantic import BaseModel + + +class REQUESTAWMXNTKMGPPOJWRCTZUBADNFLQDBDYVQAORFAVCSXUUHEBQHCEPCSKFBADBODFDBPYKOVINV( + BaseModel +): + uu_id: str + created_at: str + updated_at: str + person_tag: str + expiry_starts: str + expiry_ends: str + firstname: str + middle_name: str + surname: str + birth_date: str + birth_place: str + sex_code: str + country_code: str + tax_no: str + active: bool + deleted: bool + is_confirmed: bool + is_notification_send: bool diff --git a/ApiServices/TemplateService/Validations/user/validations.py b/ApiServices/TemplateService/Validations/user/validations.py new file mode 100644 index 0000000..a24bcd9 --- /dev/null +++ b/ApiServices/TemplateService/Validations/user/validations.py @@ -0,0 +1,33 @@ +from pydantic import BaseModel + + +class FF7C859A068EB4583A47AB5924EC8C19A033881823FBA42EAAD089BF7AC8059CE(BaseModel): + """ + a13143ade48954c2ba3f86869e027de5b28c8a9b619bf4ef28264a8e375371601 + """ + + pass + + +class F1B82565925FF4F5DAD2A36370788305A0A362E031EAC4A9E8BDFF4F35E265A6C(BaseModel): + """ + aa487ab3bfd9e4e6abc2db714ac6197f60bbc9068ac6541e7a815d5b1e969796b + """ + + pass + + +class F3117E7D66FE6471C8452B97AB504EF0C29822B6395CA4D65A18FDD563F0EC8D7(BaseModel): + """ + a1bf55a214b684438a97a47e4f097ac7ae27b0dff03c4475cbd4301e24a032aac + """ + + pass + + +class F33B4DE316B8A456480DD6ED5B5E2D35A2E6FCAF74BAC40D7A2970D318B153F85(BaseModel): + """ + F33B4DE316B8A456480DD6ED5B5E2D35A2E6FCAF74BAC40D7A2970D318B153F85 + """ + + pass diff --git a/Trash/TemplateService/Dockerfile b/Trash/TemplateService/Dockerfile new file mode 100644 index 0000000..9a144d2 --- /dev/null +++ b/Trash/TemplateService/Dockerfile @@ -0,0 +1,26 @@ +FROM python:3.12-slim + +WORKDIR / + +# Install system dependencies and Poetry +RUN apt-get update && apt-get install -y --no-install-recommends gcc \ + && rm -rf /var/lib/apt/lists/* && pip install --no-cache-dir poetry + +# Copy Poetry configuration +COPY /pyproject.toml ./pyproject.toml + +# Configure Poetry and install dependencies with optimizations +RUN poetry config virtualenvs.create false \ + && poetry install --no-interaction --no-ansi --no-root --only main \ + && pip cache purge && rm -rf ~/.cache/pypoetry + +# Copy application code +COPY /ApiServices/TemplateService /ApiServices/TemplateService +COPY /Controllers /Controllers +COPY /Schemas /Schemas + +# Set Python path to include app directory +ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 + +# Run the application using the configured uvicorn server +CMD ["poetry", "run", "python", "ApiServices/TemplateService/app.py"] diff --git a/ApiServices/TemplateService/initializer/__init__.py b/Trash/TemplateService/README.md similarity index 100% rename from ApiServices/TemplateService/initializer/__init__.py rename to Trash/TemplateService/README.md diff --git a/ApiServices/TemplateService/app.py b/Trash/TemplateService/app.py similarity index 100% rename from ApiServices/TemplateService/app.py rename to Trash/TemplateService/app.py diff --git a/ApiServices/TemplateService/config.py b/Trash/TemplateService/config.py similarity index 100% rename from ApiServices/TemplateService/config.py rename to Trash/TemplateService/config.py diff --git a/ApiServices/TemplateService/create_app.py b/Trash/TemplateService/create_app.py similarity index 100% rename from ApiServices/TemplateService/create_app.py rename to Trash/TemplateService/create_app.py diff --git a/ApiServices/TemplateService/middlewares/__init__.py b/Trash/TemplateService/endpoints/__init__.py similarity index 100% rename from ApiServices/TemplateService/middlewares/__init__.py rename to Trash/TemplateService/endpoints/__init__.py diff --git a/ApiServices/TemplateService/endpoints/routes.py b/Trash/TemplateService/endpoints/routes.py similarity index 100% rename from ApiServices/TemplateService/endpoints/routes.py rename to Trash/TemplateService/endpoints/routes.py diff --git a/ApiServices/TemplateService/endpoints/test_template/route.py b/Trash/TemplateService/endpoints/test_template/route.py similarity index 100% rename from ApiServices/TemplateService/endpoints/test_template/route.py rename to Trash/TemplateService/endpoints/test_template/route.py diff --git a/ApiServices/TemplateService/events/__init__.py b/Trash/TemplateService/events/__init__.py similarity index 100% rename from ApiServices/TemplateService/events/__init__.py rename to Trash/TemplateService/events/__init__.py diff --git a/ApiServices/TemplateService/events/template/cluster.py b/Trash/TemplateService/events/template/cluster.py similarity index 100% rename from ApiServices/TemplateService/events/template/cluster.py rename to Trash/TemplateService/events/template/cluster.py diff --git a/ApiServices/TemplateService/events/template/some_level_event.py b/Trash/TemplateService/events/template/some_level_event.py similarity index 100% rename from ApiServices/TemplateService/events/template/some_level_event.py rename to Trash/TemplateService/events/template/some_level_event.py diff --git a/ApiServices/TemplateService/providers/__init__.py b/Trash/TemplateService/exception_handlers/__init__.py similarity index 100% rename from ApiServices/TemplateService/providers/__init__.py rename to Trash/TemplateService/exception_handlers/__init__.py diff --git a/ApiServices/TemplateService/schemas/__init__.py b/Trash/TemplateService/initializer/__init__.py similarity index 100% rename from ApiServices/TemplateService/schemas/__init__.py rename to Trash/TemplateService/initializer/__init__.py diff --git a/ApiServices/TemplateService/initializer/create_route.py b/Trash/TemplateService/initializer/create_route.py similarity index 100% rename from ApiServices/TemplateService/initializer/create_route.py rename to Trash/TemplateService/initializer/create_route.py diff --git a/ApiServices/TemplateService/initializer/event_clusters.py b/Trash/TemplateService/initializer/event_clusters.py similarity index 100% rename from ApiServices/TemplateService/initializer/event_clusters.py rename to Trash/TemplateService/initializer/event_clusters.py diff --git a/ApiServices/TemplateService/services/__init__.py b/Trash/TemplateService/middlewares/__init__.py similarity index 100% rename from ApiServices/TemplateService/services/__init__.py rename to Trash/TemplateService/middlewares/__init__.py diff --git a/ApiServices/TemplateService/middlewares/token_middleware.py b/Trash/TemplateService/middlewares/token_middleware.py similarity index 100% rename from ApiServices/TemplateService/middlewares/token_middleware.py rename to Trash/TemplateService/middlewares/token_middleware.py diff --git a/ApiServices/TemplateService/open_api_creator.py b/Trash/TemplateService/open_api_creator.py similarity index 100% rename from ApiServices/TemplateService/open_api_creator.py rename to Trash/TemplateService/open_api_creator.py diff --git a/ApiServices/TemplateService/validations/__init__.py b/Trash/TemplateService/providers/__init__.py similarity index 100% rename from ApiServices/TemplateService/validations/__init__.py rename to Trash/TemplateService/providers/__init__.py diff --git a/ApiServices/TemplateService/providers/token_provider.py b/Trash/TemplateService/providers/token_provider.py similarity index 100% rename from ApiServices/TemplateService/providers/token_provider.py rename to Trash/TemplateService/providers/token_provider.py diff --git a/ApiServices/__init__.py b/Trash/TemplateService/schemas/__init__.py similarity index 100% rename from ApiServices/__init__.py rename to Trash/TemplateService/schemas/__init__.py diff --git a/Trash/TemplateService/services/__init__.py b/Trash/TemplateService/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Trash/TemplateService/validations/__init__.py b/Trash/TemplateService/validations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ApiServices/TemplateService/validations/lists/validations.py b/Trash/TemplateService/validations/lists/validations.py similarity index 100% rename from ApiServices/TemplateService/validations/lists/validations.py rename to Trash/TemplateService/validations/lists/validations.py diff --git a/WebServices/management-frontend/src/apicalls/api-fetcher.ts b/WebServices/management-frontend/src/apicalls/api-fetcher.ts new file mode 100644 index 0000000..112a305 --- /dev/null +++ b/WebServices/management-frontend/src/apicalls/api-fetcher.ts @@ -0,0 +1,177 @@ +"use server"; +import { retrieveAccessToken } from "@/apicalls/cookies/token"; +import { + DEFAULT_RESPONSE, + defaultHeaders, + FetchOptions, + HttpMethod, + ApiResponse, + DEFAULT_TIMEOUT, +} from "./basics"; + +/** + * Creates a promise that rejects after a specified timeout + * @param ms Timeout in milliseconds + * @param controller AbortController to abort the fetch request + * @returns A promise that rejects after the timeout + */ +const createTimeoutPromise = ( + ms: number, + controller: AbortController +): Promise => { + return new Promise((_, reject) => { + setTimeout(() => { + controller.abort(); + reject(new Error(`Request timed out after ${ms}ms`)); + }, ms); + }); +}; + +/** + * Prepares a standardized API response + * @param response The response data + * @param statusCode HTTP status code + * @returns Standardized API response + */ +const prepareResponse = ( + response: T, + statusCode: number +): ApiResponse => { + try { + return { + status: statusCode, + data: response || ({} as T), + }; + } catch (error) { + console.error("Error preparing response:", error); + return { + ...DEFAULT_RESPONSE, + error: "Response parsing error", + } as ApiResponse; + } +}; + +/** + * Core fetch function with timeout and error handling + * @param url The URL to fetch + * @param options Fetch options + * @param headers Request headers + * @param payload Request payload + * @returns API response + */ +async function coreFetch( + url: string, + options: FetchOptions = {}, + headers: Record = defaultHeaders, + payload?: any +): Promise> { + const { method = "POST", cache = false, timeout = DEFAULT_TIMEOUT } = options; + + try { + const controller = new AbortController(); + const signal = controller.signal; + + const fetchOptions: RequestInit = { + method, + headers, + cache: cache ? "force-cache" : "no-cache", + signal, + }; + + // Add body if needed + if (method !== "GET" && payload) { + fetchOptions.body = JSON.stringify( + // Handle special case for updateDataWithToken + payload.payload ? payload.payload : payload + ); + } + + // Create timeout promise + const timeoutPromise = createTimeoutPromise(timeout, controller); + + // Race between fetch and timeout + const response = (await Promise.race([ + fetch(url, fetchOptions), + timeoutPromise, + ])) as Response; + + const responseJson = await response.json(); + + if (process.env.NODE_ENV !== "production") { + console.log("Fetching:", url, fetchOptions); + console.log("Response:", responseJson); + } + + return prepareResponse(responseJson, response.status); + } catch (error) { + console.error(`Fetch error (${url}):`, error); + return { + ...DEFAULT_RESPONSE, + error: error instanceof Error ? error.message : "Network error", + } as ApiResponse; + } +} + +/** + * Fetch data without authentication + */ +async function fetchData( + endpoint: string, + payload?: any, + method: HttpMethod = "POST", + cache: boolean = false, + timeout: number = DEFAULT_TIMEOUT +): Promise> { + return coreFetch( + endpoint, + { method, cache, timeout }, + defaultHeaders, + payload + ); +} + +/** + * Fetch data with authentication token + */ +async function fetchDataWithToken( + endpoint: string, + payload?: any, + method: HttpMethod = "POST", + cache: boolean = false, + timeout: number = DEFAULT_TIMEOUT +): Promise> { + const accessToken = (await retrieveAccessToken()) || ""; + const headers = { + ...defaultHeaders, + "eys-acs-tkn": accessToken, + }; + + return coreFetch(endpoint, { method, cache, timeout }, headers, payload); +} + +/** + * Update data with authentication token and UUID + */ +async function updateDataWithToken( + endpoint: string, + uuid: string, + payload?: any, + method: HttpMethod = "POST", + cache: boolean = false, + timeout: number = DEFAULT_TIMEOUT +): Promise> { + const accessToken = (await retrieveAccessToken()) || ""; + const headers = { + ...defaultHeaders, + "eys-acs-tkn": accessToken, + }; + + return coreFetch( + `${endpoint}/${uuid}`, + { method, cache, timeout }, + headers, + payload + ); +} + +export { fetchData, fetchDataWithToken, updateDataWithToken }; diff --git a/WebServices/management-frontend/src/apicalls/api-fetcher.tsx b/WebServices/management-frontend/src/apicalls/api-fetcher.tsx deleted file mode 100644 index 4c6b490..0000000 --- a/WebServices/management-frontend/src/apicalls/api-fetcher.tsx +++ /dev/null @@ -1,144 +0,0 @@ -"use server"; -import { retrieveAccessToken } from "@/apicalls/cookies/token"; - -const defaultHeaders = { - accept: "application/json", - language: "tr", - domain: "management.com.tr", - tz: "GMT+3", - "Content-type": "application/json", -}; - -const DefaultResponse = { - error: "Hata tipi belirtilmedi", - status: "500", - data: {}, -}; - -const cacheList = ["no-cache", "no-store", "force-cache", "only-if-cached"]; - -const prepareResponse = (response: any, statusCode: number) => { - try { - return { - status: statusCode, - data: response || {}, - }; - } catch (error) { - console.error("Error preparing response:", error); - return { - ...DefaultResponse, - error: "Response parsing error", - }; - } -}; - -const fetchData = async ( - endpoint: string, - payload: any, - method: string = "POST", - cache: boolean = false -) => { - try { - const headers = { - ...defaultHeaders, - }; - const fetchOptions: RequestInit = { - method, - headers, - cache: cache ? "force-cache" : "no-cache", - }; - - if (method === "POST" && payload) { - fetchOptions.body = JSON.stringify(payload); - } - - const response = await fetch(endpoint, fetchOptions); - const responseJson = await response.json(); - console.log("Fetching:", endpoint, fetchOptions); - console.log("Response:", responseJson); - return prepareResponse(responseJson, response.status); - } catch (error) { - console.error("Fetch error:", error); - return { - ...DefaultResponse, - error: error instanceof Error ? error.message : "Network error", - }; - } -}; - -const updateDataWithToken = async ( - endpoint: string, - uuid: string, - payload: any, - method: string = "POST", - cache: boolean = false -) => { - const accessToken = (await retrieveAccessToken()) || ""; - const headers = { - ...defaultHeaders, - "eys-acs-tkn": accessToken, - }; - - try { - const fetchOptions: RequestInit = { - method, - headers, - cache: cache ? "force-cache" : "no-cache", - }; - - if (method !== "GET" && payload) { - fetchOptions.body = JSON.stringify(payload.payload); - } - - const response = await fetch(`${endpoint}/${uuid}`, fetchOptions); - const responseJson = await response.json(); - console.log("Fetching:", `${endpoint}/${uuid}`, fetchOptions); - console.log("Response:", responseJson); - return prepareResponse(responseJson, response.status); - } catch (error) { - console.error("Update error:", error); - return { - ...DefaultResponse, - error: error instanceof Error ? error.message : "Network error", - }; - } -}; - -const fetchDataWithToken = async ( - endpoint: string, - payload: any, - method: string = "POST", - cache: boolean = false -) => { - const accessToken = (await retrieveAccessToken()) || ""; - const headers = { - ...defaultHeaders, - "eys-acs-tkn": accessToken, - }; - - try { - const fetchOptions: RequestInit = { - method, - headers, - cache: cache ? "force-cache" : "no-cache", - }; - - if (method === "POST" && payload) { - fetchOptions.body = JSON.stringify(payload); - } - - const response = await fetch(endpoint, fetchOptions); - const responseJson = await response.json(); - console.log("Fetching:", endpoint, fetchOptions); - console.log("Response:", responseJson); - return prepareResponse(responseJson, response.status); - } catch (error) { - console.error("Fetch with token error:", error); - return { - ...DefaultResponse, - error: error instanceof Error ? error.message : "Network error", - }; - } -}; - -export { fetchData, fetchDataWithToken, updateDataWithToken }; diff --git a/WebServices/management-frontend/src/apicalls/basics.ts b/WebServices/management-frontend/src/apicalls/basics.ts index 9503f59..c3b0916 100644 --- a/WebServices/management-frontend/src/apicalls/basics.ts +++ b/WebServices/management-frontend/src/apicalls/basics.ts @@ -3,22 +3,34 @@ const formatServiceUrl = (url: string) => { return url.startsWith("http") ? url : `http://${url}`; }; -export const baseUrlAuth = formatServiceUrl( +const baseUrlAuth = formatServiceUrl( process.env.NEXT_PUBLIC_AUTH_SERVICE_URL || "auth_service:8001" ); -export const baseUrlPeople = formatServiceUrl( +const baseUrlPeople = formatServiceUrl( process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "identity_service:8002" ); -export const baseUrlApplication = formatServiceUrl( +const baseUrlApplication = formatServiceUrl( process.env.NEXT_PUBLIC_MANAGEMENT_SERVICE_URL || "management_service:8004" ); -// export const baseUrlEvent = formatServiceUrl( -// process.env.NEXT_PUBLIC_EVENT_SERVICE_URL || "eventservice:8888" -// ); -export const tokenSecret = process.env.TOKENSECRET_90 || ""; +// Types for better type safety +type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; -export const cookieObject: any = { +interface ApiResponse { + status: number; + data: T; + error?: string; +} + +interface FetchOptions { + method?: HttpMethod; + cache?: boolean; + timeout?: number; +} + +const tokenSecret = process.env.TOKENSECRET_90 || ""; + +const cookieObject: any = { httpOnly: true, path: "/", sameSite: "none", @@ -75,5 +87,36 @@ class FilterList { const defaultFilterList = new FilterList({}); -export { FilterList, defaultFilterList }; -export type { FilterListInterface }; +// Constants +const DEFAULT_TIMEOUT = 10000; // 10 seconds +const defaultHeaders = { + accept: "application/json", + language: "tr", + domain: "management.com.tr", + tz: "GMT+3", + "Content-type": "application/json", +}; +const DEFAULT_RESPONSE: ApiResponse = { + error: "Hata tipi belirtilmedi", + status: 500, + data: {}, +}; + +export type { + FilterList, + FilterListInterface, + HttpMethod, + ApiResponse, + FetchOptions, +}; +export { + DEFAULT_TIMEOUT, + DEFAULT_RESPONSE, + defaultHeaders, + defaultFilterList, + baseUrlAuth, + baseUrlPeople, + baseUrlApplication, + tokenSecret, + cookieObject, +}; diff --git a/WebServices/management-frontend/src/app/api/applications/create/route.ts b/WebServices/management-frontend/src/app/api/applications/create/route.ts new file mode 100644 index 0000000..fec4690 --- /dev/null +++ b/WebServices/management-frontend/src/app/api/applications/create/route.ts @@ -0,0 +1,9 @@ +import { NextRequest } from "next/server"; +// Import the createApplication function when it's available +// import { createApplication } from "@/apicalls/application/application"; +import { createCreateHandler } from "../../utils"; + +// Create a handler for creating applications using our utility function +// When createApplication is available, pass it as the first argument +// No need for field validation as it's already handled by Zod at the page level +export const POST = createCreateHandler(); diff --git a/WebServices/management-frontend/src/app/api/applications/list/route.ts b/WebServices/management-frontend/src/app/api/applications/list/route.ts new file mode 100644 index 0000000..02a7b5d --- /dev/null +++ b/WebServices/management-frontend/src/app/api/applications/list/route.ts @@ -0,0 +1,6 @@ +import { NextRequest } from "next/server"; +import { listApplications } from "@/apicalls/application/application"; +import { createListHandler } from "../../utils"; + +// Create a handler for listing applications using our utility function +export const POST = createListHandler(listApplications); diff --git a/WebServices/management-frontend/src/app/api/applications/route.ts b/WebServices/management-frontend/src/app/api/applications/route.ts deleted file mode 100644 index e21a25f..0000000 --- a/WebServices/management-frontend/src/app/api/applications/route.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { listApplications } from "@/apicalls/application/application"; - -export async function POST(request: NextRequest) { - try { - const requestBody = await request.json(); - - // Check if this is a list request or a create request - // If action is 'list' or if pagination parameters are present, treat as a list request - if (requestBody.action === 'list' || requestBody.page !== undefined) { - // Extract pagination parameters with defaults - const page = requestBody.page || 1; - const size = requestBody.size || 10; - - // Extract sorting parameters with defaults - const orderField = requestBody.orderField || ["name"]; - const orderType = requestBody.orderType || ["asc"]; - - // Extract query filters - const query = requestBody.query || {}; - - // Call the actual API function for listing - const response = await listApplications({ - page, - size, - orderField, - orderType, - query, - }); - - // Return the list response - return NextResponse.json({ - data: response.data || [], - pagination: response.pagination || { - page, - size, - totalCount: 0, - totalItems: 0, - totalPages: 0, - pageCount: 0, - orderField, - orderType, - query, - next: false, - back: false, - }, - }); - } - // If action is 'create' or no action is specified (assuming it's a create request) - else if (requestBody.action === 'create' || !requestBody.action) { - // Here you would call your actual API function to create a new application - // For example: const result = await createApplication(requestBody.data); - - // For now, we'll return a mock response - return NextResponse.json( - { - success: true, - data: { - id: Math.floor(Math.random() * 1000), - ...requestBody.data, - createdAt: new Date().toISOString(), - }, - }, - { status: 201 } - ); - } - // If the action is not recognized - else { - return NextResponse.json( - { error: "Invalid action specified" }, - { status: 400 } - ); - } - } catch (error) { - console.error("API error:", error); - return NextResponse.json( - { error: "Internal Server Error" }, - { status: 500 } - ); - } -} diff --git a/WebServices/management-frontend/src/app/api/applications/update/route.ts b/WebServices/management-frontend/src/app/api/applications/update/route.ts new file mode 100644 index 0000000..0a2f4d5 --- /dev/null +++ b/WebServices/management-frontend/src/app/api/applications/update/route.ts @@ -0,0 +1,10 @@ +import { NextRequest } from "next/server"; +// Import the updateApplication function when it's available +// import { updateApplication } from "@/apicalls/application/application"; +import { createUpdateHandler } from "../../utils"; + +// Create a handler for updating applications using our utility function +// When updateApplication is available, pass it as the first argument +// No need for field validation as it's already handled by Zod at the page level +// We only need to ensure 'id' is present for updates +export const POST = createUpdateHandler(); diff --git a/WebServices/management-frontend/src/app/api/utils/apiOperations.ts b/WebServices/management-frontend/src/app/api/utils/apiOperations.ts new file mode 100644 index 0000000..19a043b --- /dev/null +++ b/WebServices/management-frontend/src/app/api/utils/apiOperations.ts @@ -0,0 +1,192 @@ +import { NextRequest } from "next/server"; +import { + successResponse, + errorResponse, + paginationResponse, + createResponse, + updateResponse, + deleteResponse +} from "./responseHandlers"; +import { withErrorHandling, validateRequiredFields } from "./requestHandlers"; +import { + ApiHandler, + PaginationParams, + ListFunction, + CreateFunction, + UpdateFunction, + DeleteFunction +} from "./types"; + +/** + * Generic list operation handler + * @param request NextRequest object + * @param body Request body + * @param listFunction The function to call to get the list data + */ +export async function handleListOperation( + request: NextRequest, + body: any, + listFunction: ListFunction +) { + // Extract pagination parameters with defaults + const page = body.page || 1; + const size = body.size || 10; + + // Extract sorting parameters with defaults + const orderField = body.orderField || ["name"]; + const orderType = body.orderType || ["asc"]; + + // Extract query filters + const query = body.query || {}; + + // Call the actual API function for listing + const response = await listFunction({ + page, + size, + orderField, + orderType, + query, + } as PaginationParams); + + // Return the list response + return paginationResponse(response.data, response.pagination); +} + +/** + * Generic create operation handler + * @param request NextRequest object + * @param body Request body + * @param createFunction The function to call to create the item + * @param requiredFields Array of required field names + */ +export async function handleCreateOperation( + request: NextRequest, + body: any, + createFunction?: CreateFunction, + requiredFields: string[] = [] +) { + // Validate required fields if specified + if (requiredFields.length > 0) { + const validation = validateRequiredFields(body, requiredFields); + if (!validation.valid) { + return errorResponse(validation.error as string, 400); + } + } + + // If a create function is provided, call it + if (createFunction) { + const result = await createFunction(body); + return createResponse(result); + } + + // Otherwise return a mock response + return createResponse({ + id: Math.floor(Math.random() * 1000), + ...body, + }); +} + +/** + * Generic update operation handler + * @param request NextRequest object + * @param body Request body + * @param updateFunction The function to call to update the item + * @param requiredFields Array of required field names + */ +export async function handleUpdateOperation( + request: NextRequest, + body: any, + updateFunction?: UpdateFunction, + requiredFields: string[] = ['id'] +) { + // Validate required fields + const validation = validateRequiredFields(body, requiredFields); + if (!validation.valid) { + return errorResponse(validation.error as string, 400); + } + + // If an update function is provided, call it + if (updateFunction) { + const result = await updateFunction(body.id, body); + return updateResponse(result); + } + + // Otherwise return a mock response + return updateResponse(body); +} + +/** + * Generic delete operation handler + * @param request NextRequest object + * @param body Request body + * @param deleteFunction The function to call to delete the item + */ +export async function handleDeleteOperation( + request: NextRequest, + body: any, + deleteFunction?: DeleteFunction +) { + // Validate that ID is provided + const validation = validateRequiredFields(body, ['id']); + if (!validation.valid) { + return errorResponse(validation.error as string, 400); + } + + // If a delete function is provided, call it + if (deleteFunction) { + await deleteFunction(body.id); + } + + // Return a success response + return deleteResponse(); +} + +/** + * Create a wrapped list handler with error handling + * @param listFunction The function to call to get the list data + */ +export function createListHandler(listFunction: ListFunction) { + return withErrorHandling((request, body) => + handleListOperation(request, body, listFunction) + ); +} + +/** + * Create a wrapped create handler with error handling + * @param createFunction The function to call to create the item + * @param requiredFields Array of required field names + */ +export function createCreateHandler( + createFunction?: CreateFunction, + requiredFields: string[] = [] +) { + return withErrorHandling((request, body) => + handleCreateOperation(request, body, createFunction, requiredFields) + ); +} + +/** + * Create a wrapped update handler with error handling + * @param updateFunction The function to call to update the item + * @param requiredFields Array of required field names + */ +export function createUpdateHandler( + updateFunction?: UpdateFunction, + requiredFields: string[] = ['id'] +) { + return withErrorHandling((request, body) => + handleUpdateOperation(request, body, updateFunction, requiredFields) + ); +} + +/** + * Create a wrapped delete handler with error handling + * @param deleteFunction The function to call to delete the item + */ +export function createDeleteHandler( + deleteFunction?: DeleteFunction +) { + return withErrorHandling((request, body) => + handleDeleteOperation(request, body, deleteFunction) + ); +} diff --git a/WebServices/management-frontend/src/app/api/utils/index.ts b/WebServices/management-frontend/src/app/api/utils/index.ts new file mode 100644 index 0000000..373c546 --- /dev/null +++ b/WebServices/management-frontend/src/app/api/utils/index.ts @@ -0,0 +1,4 @@ +// Export all utility functions from a single entry point +export * from './responseHandlers'; +export * from './requestHandlers'; +export * from './apiOperations'; diff --git a/WebServices/management-frontend/src/app/api/utils/requestHandlers.ts b/WebServices/management-frontend/src/app/api/utils/requestHandlers.ts new file mode 100644 index 0000000..77a5be3 --- /dev/null +++ b/WebServices/management-frontend/src/app/api/utils/requestHandlers.ts @@ -0,0 +1,62 @@ +import { NextRequest } from "next/server"; +import { errorResponse } from "./responseHandlers"; +import { ValidationResult, ApiHandler } from "./types"; + +/** + * Safely parse JSON request body with error handling + * @param request NextRequest object + * @returns Parsed request body or null if parsing fails + */ +export async function parseRequestBody(request: NextRequest) { + try { + return await request.json(); + } catch (error) { + return null; + } +} + +/** + * Wrapper for API route handlers with built-in error handling + * @param handler The handler function to wrap + */ +export function withErrorHandling( + handler: ApiHandler +) { + return async (request: NextRequest) => { + try { + const body = await parseRequestBody(request); + + if (body === null) { + return errorResponse("Invalid request body", 400); + } + + return await handler(request, body); + } catch (error: any) { + return errorResponse( + error.message || "Internal Server Error", + error.status || 500 + ); + } + }; +} + +/** + * Validate that required fields are present in the request body + * @param body Request body + * @param requiredFields Array of required field names + * @returns Object with validation result and error message if validation fails + */ +export function validateRequiredFields(body: any, requiredFields: string[]): ValidationResult { + const missingFields = requiredFields.filter(field => + body[field] === undefined || body[field] === null || body[field] === '' + ); + + if (missingFields.length > 0) { + return { + valid: false, + error: `Missing required fields: ${missingFields.join(', ')}` + }; + } + + return { valid: true }; +} diff --git a/WebServices/management-frontend/src/app/api/utils/responseHandlers.ts b/WebServices/management-frontend/src/app/api/utils/responseHandlers.ts new file mode 100644 index 0000000..45cfc42 --- /dev/null +++ b/WebServices/management-frontend/src/app/api/utils/responseHandlers.ts @@ -0,0 +1,91 @@ +import { NextResponse } from "next/server"; +import { ApiResponse, PaginationResponse, PaginatedApiResponse } from "./types"; + +/** + * Standard success response handler + * @param data The data to return in the response + * @param status HTTP status code (default: 200) + */ +export function successResponse(data: T, status: number = 200) { + return NextResponse.json( + { + success: true, + data, + } as ApiResponse, + { status } + ); +} + +/** + * Standard error response handler + * @param message Error message + * @param status HTTP status code (default: 500) + */ +export function errorResponse(message: string, status: number = 500) { + console.error(`API error: ${message}`); + return NextResponse.json( + { + success: false, + error: message + } as ApiResponse, + { status } + ); +} + +/** + * Standard pagination response format + * @param data Array of items to return + * @param pagination Pagination information + */ +export function paginationResponse(data: T[], pagination: PaginationResponse | null) { + return NextResponse.json({ + data: data || [], + pagination: pagination || { + page: 1, + size: 10, + totalCount: 0, + totalItems: 0, + totalPages: 0, + pageCount: 0, + orderField: ["name"], + orderType: ["asc"], + query: {}, + next: false, + back: false, + }, + } as PaginatedApiResponse); +} + +/** + * Create response handler + * @param data The created item data + */ +export function createResponse(data: T) { + return successResponse( + { + ...data as any, + createdAt: new Date().toISOString(), + } as T, + 201 + ); +} + +/** + * Update response handler + * @param data The updated item data + */ +export function updateResponse(data: T) { + return successResponse( + { + ...data as any, + updatedAt: new Date().toISOString(), + } as T + ); +} + +/** + * Delete response handler + */ +export function deleteResponse() { + return successResponse({ message: "Item deleted successfully" }, 204); +} diff --git a/WebServices/management-frontend/src/app/api/utils/types.ts b/WebServices/management-frontend/src/app/api/utils/types.ts new file mode 100644 index 0000000..7e82555 --- /dev/null +++ b/WebServices/management-frontend/src/app/api/utils/types.ts @@ -0,0 +1,83 @@ +/** + * Type definitions for API utilities + */ + +import { NextRequest } from "next/server"; + +/** + * Validation result interface + */ +export interface ValidationResult { + valid: boolean; + error?: string; +} + +/** + * Pagination parameters interface + */ +export interface PaginationParams { + page: number; + size: number; + orderField: string[]; + orderType: string[]; + query: Record; +} + +/** + * Pagination response interface + */ +export interface PaginationResponse { + page: number; + size: number; + totalCount: number; + totalItems: number; + totalPages: number; + pageCount: number; + orderField: string[]; + orderType: string[]; + query: Record; + next: boolean; + back: boolean; +} + +/** + * API response interface + */ +export interface ApiResponse { + success: boolean; + data?: T; + error?: string; +} + +/** + * Paginated API response interface + */ +export interface PaginatedApiResponse { + data: T[]; + pagination: PaginationResponse; +} + +/** + * API handler function type + */ +export type ApiHandler = (request: NextRequest, body: any) => Promise; + +/** + * List function type + */ +export type ListFunction = (params: PaginationParams) => Promise>; + +/** + * Create function type + */ +export type CreateFunction = (data: any) => Promise; + +/** + * Update function type + */ +export type UpdateFunction = (id: any, data: any) => Promise; + +/** + * Delete function type + */ +export type DeleteFunction = (id: any) => Promise; diff --git a/WebServices/management-frontend/src/components/common/FormDisplay/CreateComponent.tsx b/WebServices/management-frontend/src/components/common/FormDisplay/CreateComponent.tsx index 4968cc2..f4debcc 100644 --- a/WebServices/management-frontend/src/components/common/FormDisplay/CreateComponent.tsx +++ b/WebServices/management-frontend/src/components/common/FormDisplay/CreateComponent.tsx @@ -9,7 +9,7 @@ import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Checkbox } from "@/components/ui/checkbox"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; +import { useForm, SubmitHandler } from "react-hook-form"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; @@ -21,6 +21,7 @@ export function CreateComponent({ lang, translations, formProps = {}, + apiUrl, }: CreateComponentProps) { const t = translations[lang as keyof typeof translations] || {}; @@ -63,7 +64,7 @@ export function CreateComponent({ formState: { errors }, setValue, watch, - } = useForm({ + } = useForm>({ defaultValues, resolver: validationSchema ? zodResolver(validationSchema) : undefined, }); @@ -71,14 +72,30 @@ export function CreateComponent({ const formValues = watch(); // Handle form submission - const onSubmit = async (data: Record) => { + const onSubmit: SubmitHandler> = async (data) => { try { console.log("Form data to save:", data); - // Here you would make an API call to save the data - // For example: await createApplication(data); + // Make an API call to save the data if apiUrl is provided + if (apiUrl) { + // Use the create endpoint by appending '/create' to the apiUrl + const createUrl = `${apiUrl}/create`; + const response = await fetch(createUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); + + if (!response.ok) { + throw new Error(`API error: ${response.status}`); + } + + // Optional: get the created item from response + // const createdItem = await response.json(); + } - // Mock API call success if (refetch) refetch(); setMode("list"); setSelectedItem(null); @@ -219,7 +236,7 @@ export function CreateComponent({ }; return ( -
console.log("Form data to save:"))}> + {t.create || "Create"} diff --git a/WebServices/management-frontend/src/components/common/FormDisplay/FormDisplay.tsx b/WebServices/management-frontend/src/components/common/FormDisplay/FormDisplay.tsx index 20f7740..aec3b75 100644 --- a/WebServices/management-frontend/src/components/common/FormDisplay/FormDisplay.tsx +++ b/WebServices/management-frontend/src/components/common/FormDisplay/FormDisplay.tsx @@ -15,6 +15,7 @@ export function FormDisplay({ lang, translations, formProps = {}, + apiUrl, }: FormDisplayProps) { const [enhancedFormProps, setEnhancedFormProps] = useState(formProps); @@ -84,6 +85,7 @@ export function FormDisplay({ lang={lang} translations={translations} formProps={enhancedFormProps} + apiUrl={apiUrl} /> ); case "update": diff --git a/WebServices/management-frontend/src/components/common/FormDisplay/types.ts b/WebServices/management-frontend/src/components/common/FormDisplay/types.ts index 730d9ca..1c8b3b2 100644 --- a/WebServices/management-frontend/src/components/common/FormDisplay/types.ts +++ b/WebServices/management-frontend/src/components/common/FormDisplay/types.ts @@ -24,6 +24,7 @@ export interface BaseFormProps { lang: string; translations: Record>; formProps?: Record; + apiUrl?: string; } export interface CreateComponentProps extends BaseFormProps {} @@ -46,4 +47,5 @@ export interface FormDisplayProps { lang: string; translations: Record>; formProps?: Record; + apiUrl?: string; } diff --git a/WebServices/management-frontend/src/components/common/hooks/useApiData.ts b/WebServices/management-frontend/src/components/common/hooks/useApiData.ts index 46448eb..ae17e1a 100644 --- a/WebServices/management-frontend/src/components/common/hooks/useApiData.ts +++ b/WebServices/management-frontend/src/components/common/hooks/useApiData.ts @@ -16,9 +16,8 @@ export function useApiData( params: RequestParams ): Promise> => { try { - // Prepare the request body with action and all params + // Prepare the request body with pagination parameters const requestBody = { - action: "list", page: params.page, size: params.size, orderField: params.orderField, @@ -26,8 +25,11 @@ export function useApiData( query: params.query, }; + // Construct the list endpoint URL + const listEndpoint = `${endpoint}/list`; + // Make the API request using POST - const response = await fetch(endpoint, { + const response = await fetch(listEndpoint, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/WebServices/management-frontend/src/eventRouters/application/page.tsx b/WebServices/management-frontend/src/eventRouters/application/page.tsx index 31b996c..dbf74bc 100644 --- a/WebServices/management-frontend/src/eventRouters/application/page.tsx +++ b/WebServices/management-frontend/src/eventRouters/application/page.tsx @@ -1,11 +1,12 @@ "use client"; import React, { useState } from "react"; +import * as schema from "./schema"; +import { translations } from "./language"; +import type { FormMode } from "@/components/common/FormDisplay/types"; + import { PageProps } from "@/validations/translations/translation"; import { useApiData } from "@/components/common/hooks/useApiData"; -import { translations } from "./language"; -import * as schema from "./schema"; import { Card, CardContent } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; import { Building, Filter, User } from "lucide-react"; import { TextQueryModifier, SelectQueryModifier, TypeQueryModifier } from "@/components/common/QueryModifiers"; import { CreateButton } from "@/components/common/ActionButtonsDisplay/CreateButton"; @@ -14,12 +15,11 @@ import { CardDisplay } from "@/components/common/CardDisplay"; import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay"; import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent"; import { LanguageSelectionComponent, Language } from "@/components/common/HeaderSelections/LanguageSelectionComponent"; -import type { FormMode } from "@/components/common/FormDisplay/types"; const ApplicationPage: React.FC = ({ lang: initialLang = "en" }) => { // Add local state for language to ensure it persists when changed const [lang, setLang] = useState(initialLang as Language); - + // Use the API data hook directly const { data, @@ -136,16 +136,16 @@ const ApplicationPage: React.FC = ({ lang: initialLang = "en" }) => {

{translations[lang].applicationTitle || "Applications"}

{/* Grid Selection */} - - + {/* Language Selection */} -
@@ -280,6 +280,7 @@ const ApplicationPage: React.FC = ({ lang: initialLang = "en" }) => { onCancel={handleCancel} lang={lang} translations={translations} + apiUrl='/api/applications' formProps={{ fieldDefinitions: mode === 'create' ? schema.createFieldDefinitions : mode === 'update' ? schema.updateFieldDefinitions : diff --git a/docker-compose.yml b/docker-compose.yml index 84cc2ed..b4285ba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -84,33 +84,6 @@ services: mem_limit: 512m cpus: 0.5 - # building_service: - # container_name: building_service - # build: - # context: . - # dockerfile: ApiServices/BuildingService/Dockerfile - # networks: - # - wag-services - # env_file: - # - api_env.env - # depends_on: - # - initializer_service - # environment: - # - API_PATH=app:app - # - API_HOST=0.0.0.0 - # - API_PORT=8003 - # - API_LOG_LEVEL=info - # - API_RELOAD=1 - # - API_APP_NAME=evyos-building-api-gateway - # - API_TITLE=WAG API Building Api Gateway - # - API_FORGOT_LINK=https://building_service/forgot-password - # - API_DESCRIPTION=This api is serves as web building api gateway only to evyos web services. - # - API_APP_URL=https://building_service - # ports: - # - "8003:8003" - # mem_limit: 512m - # cpus: 0.5 - management_service: container_name: management_service build: @@ -152,6 +125,33 @@ services: mem_limit: 512m cpus: 0.5 + # building_service: + # container_name: building_service + # build: + # context: . + # dockerfile: ApiServices/BuildingService/Dockerfile + # networks: + # - wag-services + # env_file: + # - api_env.env + # depends_on: + # - initializer_service + # environment: + # - API_PATH=app:app + # - API_HOST=0.0.0.0 + # - API_PORT=8003 + # - API_LOG_LEVEL=info + # - API_RELOAD=1 + # - API_APP_NAME=evyos-building-api-gateway + # - API_TITLE=WAG API Building Api Gateway + # - API_FORGOT_LINK=https://building_service/forgot-password + # - API_DESCRIPTION=This api is serves as web building api gateway only to evyos web services. + # - API_APP_URL=https://building_service + # ports: + # - "8003:8003" + # mem_limit: 512m + # cpus: 0.5 + # dealer_service: # container_name: dealer_service # build: