From 2aa36087a91ae1586ed920b4a6873b6cb6720893 Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Sun, 3 Aug 2025 19:51:54 +0530 Subject: [PATCH 1/6] added models to create graph_template --- .../app/models/db/graph_template_model.py | 23 +++++++++++++++++++ state-manager/app/models/graph_models.py | 18 +++++++++++++++ .../graph_template_validation_status.py | 8 +++++++ .../app/models/node_template_model.py | 11 +++++++++ 4 files changed, 60 insertions(+) create mode 100644 state-manager/app/models/db/graph_template_model.py create mode 100644 state-manager/app/models/graph_models.py create mode 100644 state-manager/app/models/graph_template_validation_status.py create mode 100644 state-manager/app/models/node_template_model.py diff --git a/state-manager/app/models/db/graph_template_model.py b/state-manager/app/models/db/graph_template_model.py new file mode 100644 index 00000000..fcc55868 --- /dev/null +++ b/state-manager/app/models/db/graph_template_model.py @@ -0,0 +1,23 @@ +from .base import BaseDatabaseModel +from pydantic import Field +from typing import Optional, List +from ..graph_template_validation_status import GraphTemplateValidationStatus +from ..node_template_model import NodeTemplate +from pymongo import IndexModel + + +class GraphTemplate(BaseDatabaseModel): + name: str = Field(..., description="Name of the graph") + namespace: str = Field(..., description="Namespace of the graph") + nodes: List[NodeTemplate] = Field(..., description="Nodes of the graph") + validation_status: GraphTemplateValidationStatus = Field(..., description="Validation status of the graph") + validation_errors: Optional[List[str]] = Field(None, description="Validation errors of the graph") + + class Settings: + indexes = [ + IndexModel( + keys=[("name", 1), ("namespace", 1)], + unique=True, + name="unique_name_namespace" + ) + ] \ No newline at end of file diff --git a/state-manager/app/models/graph_models.py b/state-manager/app/models/graph_models.py new file mode 100644 index 00000000..1cc873dc --- /dev/null +++ b/state-manager/app/models/graph_models.py @@ -0,0 +1,18 @@ +from .node_template_model import NodeTemplate +from pydantic import BaseModel +from typing import List +from datetime import datetime + + +class UpsertGraphTemplateRequest(BaseModel): + name: str + namespace: str + nodes: List[NodeTemplate] + + +class UpsertGraphTemplateResponse(BaseModel): + name: str + namespace: str + nodes: List[NodeTemplate] + created_at: datetime + updated_at: datetime \ No newline at end of file diff --git a/state-manager/app/models/graph_template_validation_status.py b/state-manager/app/models/graph_template_validation_status.py new file mode 100644 index 00000000..a45b3d39 --- /dev/null +++ b/state-manager/app/models/graph_template_validation_status.py @@ -0,0 +1,8 @@ +from enum import Enum + + +class GraphTemplateValidationStatus(Enum): + VALID = "VALID" + INVALID = "INVALID" + PENDING = "PENDING" + ONGOING = "ONGOING" \ No newline at end of file diff --git a/state-manager/app/models/node_template_model.py b/state-manager/app/models/node_template_model.py new file mode 100644 index 00000000..50373d0a --- /dev/null +++ b/state-manager/app/models/node_template_model.py @@ -0,0 +1,11 @@ +from pydantic import Field, BaseModel +from typing import Any, Optional, List + + +class NodeTemplate(BaseModel): + name: str = Field(..., description="Name of the node") + namespace: str = Field(..., description="Namespace of the node") + indentifier: str = Field(..., description="Indentifier of the node") + inputs: dict[str, Any] = Field(..., description="Inputs of the node") + store: dict[str, Any] = Field(..., description="Upsert data to store object for the node") + next_nodes: Optional[List[str]] = Field(None, description="Next nodes to execute") \ No newline at end of file From e023ee61bee5e3d4fbba79f0155a2611cf7239f7 Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Sun, 3 Aug 2025 19:55:33 +0530 Subject: [PATCH 2/6] fixed spell check --- state-manager/app/models/node_template_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state-manager/app/models/node_template_model.py b/state-manager/app/models/node_template_model.py index 50373d0a..5056e5a0 100644 --- a/state-manager/app/models/node_template_model.py +++ b/state-manager/app/models/node_template_model.py @@ -5,7 +5,7 @@ class NodeTemplate(BaseModel): name: str = Field(..., description="Name of the node") namespace: str = Field(..., description="Namespace of the node") - indentifier: str = Field(..., description="Indentifier of the node") + identifier: str = Field(..., description="Identifier of the node") inputs: dict[str, Any] = Field(..., description="Inputs of the node") store: dict[str, Any] = Field(..., description="Upsert data to store object for the node") next_nodes: Optional[List[str]] = Field(None, description="Next nodes to execute") \ No newline at end of file From a897eea23c42bb16ce6046e9ea85955242fde939 Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Sun, 3 Aug 2025 20:06:49 +0530 Subject: [PATCH 3/6] fixed comments by @coderabbitai --- state-manager/app/models/graph_models.py | 23 +++++++++++-------- .../app/models/node_template_model.py | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/state-manager/app/models/graph_models.py b/state-manager/app/models/graph_models.py index 1cc873dc..a4617e92 100644 --- a/state-manager/app/models/graph_models.py +++ b/state-manager/app/models/graph_models.py @@ -1,18 +1,21 @@ from .node_template_model import NodeTemplate -from pydantic import BaseModel -from typing import List +from pydantic import BaseModel, Field +from typing import List, Optional from datetime import datetime +from .graph_template_validation_status import GraphTemplateValidationStatus class UpsertGraphTemplateRequest(BaseModel): - name: str - namespace: str - nodes: List[NodeTemplate] + name: str = Field(..., description="The name of the graph template") + namespace: str = Field(..., description="The namespace where the graph template will be stored") + nodes: List[NodeTemplate] = Field(..., description="List of node templates that define the graph structure") class UpsertGraphTemplateResponse(BaseModel): - name: str - namespace: str - nodes: List[NodeTemplate] - created_at: datetime - updated_at: datetime \ No newline at end of file + name: str = Field(..., description="The name of the graph template") + namespace: str = Field(..., description="The namespace where the graph template is stored") + nodes: List[NodeTemplate] = Field(..., description="List of node templates that define the graph structure") + created_at: datetime = Field(..., description="Timestamp when the graph template was created") + updated_at: datetime = Field(..., description="Timestamp when the graph template was last updated") + validation_status: GraphTemplateValidationStatus = Field(..., description="Current validation status of the graph template") + validation_errors: Optional[List[str]] = Field(None, description="List of validation errors if the graph template is invalid") diff --git a/state-manager/app/models/node_template_model.py b/state-manager/app/models/node_template_model.py index 5056e5a0..970662b7 100644 --- a/state-manager/app/models/node_template_model.py +++ b/state-manager/app/models/node_template_model.py @@ -3,7 +3,7 @@ class NodeTemplate(BaseModel): - name: str = Field(..., description="Name of the node") + node_name: str = Field(..., description="Name of the node") namespace: str = Field(..., description="Namespace of the node") identifier: str = Field(..., description="Identifier of the node") inputs: dict[str, Any] = Field(..., description="Inputs of the node") From 1999bbc4a6ded30cfce8f5e43afb3f3c235c4aaa Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Sun, 3 Aug 2025 22:05:54 +0530 Subject: [PATCH 4/6] added graph create apis --- .../app/controller/upsert_graph_template.py | 58 +++++++++++++++++++ .../graph_template_validation_status.py | 4 +- state-manager/app/routes.py | 33 +++++++++-- 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 state-manager/app/controller/upsert_graph_template.py diff --git a/state-manager/app/controller/upsert_graph_template.py b/state-manager/app/controller/upsert_graph_template.py new file mode 100644 index 00000000..74e68b99 --- /dev/null +++ b/state-manager/app/controller/upsert_graph_template.py @@ -0,0 +1,58 @@ +from app.singletons.logs_manager import LogsManager +from app.models.graph_models import UpsertGraphTemplateRequest, UpsertGraphTemplateResponse +from app.models.db.graph_template_model import GraphTemplate +from app.models.graph_template_validation_status import GraphTemplateValidationStatus +from beanie.operators import Set + +logger = LogsManager().get_logger() + +async def upsert_graph_template(namespace_name: str, graph_name: str, body: UpsertGraphTemplateRequest, x_exosphere_request_id: str) -> UpsertGraphTemplateResponse: + try: + graph_template = await GraphTemplate.find_one( + GraphTemplate.name == graph_name, + GraphTemplate.namespace == namespace_name + ) + if graph_template: + logger.info( + "Graph template already exists in namespace", graph_template=graph_template, + namespace_name=namespace_name, + x_exosphere_request_id=x_exosphere_request_id) + + await graph_template.update( + Set({ + GraphTemplate.nodes: body.nodes, # type: ignore + GraphTemplate.validation_status: GraphTemplateValidationStatus.PENDING, # type: ignore + GraphTemplate.validation_errors: [] # type: ignore + }) + ) + + else: + logger.info( + "Graph template does not exist in namespace", + namespace_name=namespace_name, + graph_name=graph_name, + x_exosphere_request_id=x_exosphere_request_id) + + graph_template = await GraphTemplate.insert( + GraphTemplate( + name=graph_name, + namespace=namespace_name, + nodes=body.nodes, + validation_status=GraphTemplateValidationStatus.PENDING, + validation_errors=[] + ) + ) + + return UpsertGraphTemplateResponse( + name=graph_template.name, + namespace=graph_template.namespace, + nodes=graph_template.nodes, + validation_status=graph_template.validation_status, + validation_errors=graph_template.validation_errors, + created_at=graph_template.created_at, + updated_at=graph_template.updated_at + ) + + except Exception as e: + logger.error(f"Error upserting graph template", error=e, x_exosphere_request_id=x_exosphere_request_id) + raise e \ No newline at end of file diff --git a/state-manager/app/models/graph_template_validation_status.py b/state-manager/app/models/graph_template_validation_status.py index a45b3d39..604a6d3b 100644 --- a/state-manager/app/models/graph_template_validation_status.py +++ b/state-manager/app/models/graph_template_validation_status.py @@ -1,8 +1,8 @@ from enum import Enum -class GraphTemplateValidationStatus(Enum): +class GraphTemplateValidationStatus(str, Enum): VALID = "VALID" INVALID = "INVALID" PENDING = "PENDING" - ONGOING = "ONGOING" \ No newline at end of file + ONGOING = "ONGOING" diff --git a/state-manager/app/routes.py b/state-manager/app/routes.py index 4e6c5af2..3f77ae79 100644 --- a/state-manager/app/routes.py +++ b/state-manager/app/routes.py @@ -18,15 +18,18 @@ from .models.errored_models import ErroredRequestModel, ErroredResponseModel from .controller.errored_state import errored_state +from .models.graph_models import UpsertGraphTemplateRequest, UpsertGraphTemplateResponse +from .controller.upsert_graph_template import upsert_graph_template as upsert_graph_template_controller + logger = LogsManager().get_logger() -router = APIRouter(prefix="/v0/namespace/{namespace_name}/states", tags=["state"]) +router = APIRouter(prefix="/v0/namespace/{namespace_name}", tags=["state"]) @router.post( - "/enqueue", + "/states/enqueue", response_model=EnqueueResponseModel, status_code=status.HTTP_200_OK, response_description="State enqueued on node queue successfully" @@ -45,7 +48,7 @@ async def enqueue_state(namespace_name: str, body: EnqueueRequestModel, request: @router.post( - "/create", + "/states/create", response_model=CreateResponseModel, status_code=status.HTTP_200_OK, response_description="States created successfully" @@ -64,7 +67,7 @@ async def create_state(namespace_name: str, body: CreateRequestModel, request: R @router.post( - "/{state_id}/executed", + "/states/{state_id}/executed", response_model=ExecutedResponseModel, status_code=status.HTTP_200_OK, response_description="State executed successfully" @@ -83,7 +86,7 @@ async def executed_state_route(namespace_name: str, state_id: str, body: Execute @router.post( - "/{state_id}/errored", + "/states/{state_id}/errored", response_model=ErroredResponseModel, status_code=status.HTTP_200_OK, response_description="State errored successfully" @@ -98,4 +101,22 @@ async def errored_state_route(namespace_name: str, state_id: str, body: ErroredR logger.error(f"API key is invalid for namespace {namespace_name}", x_exosphere_request_id=x_exosphere_request_id) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key") - return await errored_state(namespace_name, ObjectId(state_id), body, x_exosphere_request_id) \ No newline at end of file + return await errored_state(namespace_name, ObjectId(state_id), body, x_exosphere_request_id) + + +@router.put( + "/graph-templates/{graph_name}", + response_model=UpsertGraphTemplateResponse, + status_code=status.HTTP_200_OK, + response_description="Graph template upserted successfully" +) +async def upsert_graph_template(namespace_name: str, graph_name: str, body: UpsertGraphTemplateRequest, request: Request, api_key: str = Depends(check_api_key)): + x_exosphere_request_id = getattr(request.state, "x_exosphere_request_id", str(uuid4())) + + if api_key: + logger.info(f"API key is valid for namespace {namespace_name}", x_exosphere_request_id=x_exosphere_request_id) + else: + logger.error(f"API key is invalid for namespace {namespace_name}", x_exosphere_request_id=x_exosphere_request_id) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key") + + return await upsert_graph_template_controller(namespace_name, graph_name, body, x_exosphere_request_id) \ No newline at end of file From 3f122f73b6d1af5ba03747c022e9888a70cb3447 Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Sun, 3 Aug 2025 22:07:35 +0530 Subject: [PATCH 5/6] removed extra f --- state-manager/app/controller/upsert_graph_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state-manager/app/controller/upsert_graph_template.py b/state-manager/app/controller/upsert_graph_template.py index 74e68b99..a04bceaf 100644 --- a/state-manager/app/controller/upsert_graph_template.py +++ b/state-manager/app/controller/upsert_graph_template.py @@ -54,5 +54,5 @@ async def upsert_graph_template(namespace_name: str, graph_name: str, body: Upse ) except Exception as e: - logger.error(f"Error upserting graph template", error=e, x_exosphere_request_id=x_exosphere_request_id) + logger.error("Error upserting graph template", error=e, x_exosphere_request_id=x_exosphere_request_id) raise e \ No newline at end of file From 12a10dfae097303a61176943a6613fec9923221b Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Sun, 3 Aug 2025 22:08:55 +0530 Subject: [PATCH 6/6] added GrpahTemplate to main --- state-manager/app/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/state-manager/app/main.py b/state-manager/app/main.py index 1fa99ddf..f934f321 100644 --- a/state-manager/app/main.py +++ b/state-manager/app/main.py @@ -20,6 +20,7 @@ # injecting models from .models.db.state import State from .models.db.namespace import Namespace +from .models.db.graph_template_model import GraphTemplate # injecting routes from .routes import router @@ -35,7 +36,7 @@ async def lifespan(app: FastAPI): # initializing beanie client = AsyncMongoClient(os.getenv("MONGO_URI")) db = client[os.getenv("MONGO_DATABASE_NAME", "exosphere-state-manager")] - await init_beanie(db, document_models=[State, Namespace]) + await init_beanie(db, document_models=[State, Namespace, GraphTemplate]) logger.info("beanie dbs initialized") # initialize secret