Compare commits
3 Commits
VORKOUT-31
...
VORKOUT-28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5b71361ad | ||
|
|
362ff7babe | ||
| fe7f8c6511 |
@@ -88,20 +88,12 @@ async def create_ps_node_schema(
|
||||
return await get_last_ps_node_by_creator_and_ps_id(connection, creator_id, validated_schema.ps_id)
|
||||
|
||||
|
||||
async def check_node_connection(connection: AsyncConnection, node_id: int, next_node_id: int, port: int) -> bool:
|
||||
async def update_ps_node_by_id(connection: AsyncConnection, update_values: dict, ps_node: Ps_Node) -> None:
|
||||
"""
|
||||
Проверяет, подключен ли next_node_id к node_id через указанный порт.
|
||||
Обновляет данные PS ноды в таблице ps_node_table.
|
||||
"""
|
||||
query = select(node_link_table).where(
|
||||
and_(
|
||||
node_link_table.c.node_id == node_id,
|
||||
node_link_table.c.next_node_id == next_node_id,
|
||||
node_link_table.c.link_point_id == port,
|
||||
)
|
||||
)
|
||||
|
||||
result = await connection.execute(query)
|
||||
return result.mappings().first() is not None
|
||||
await connection.execute(update(ps_node_table).where(ps_node_table.c.id == ps_node.id).values(**update_values))
|
||||
await connection.commit()
|
||||
|
||||
|
||||
async def get_nodes_for_deletion_ordered(connection: AsyncConnection, node_id: int) -> list[int]:
|
||||
|
||||
@@ -5,9 +5,15 @@ from sqlalchemy.ext.asyncio import AsyncConnection
|
||||
from api.db.connection.session import get_connection_dep
|
||||
from api.db.logic.account import get_user_by_login
|
||||
from api.schemas.base import bearer_schema
|
||||
from api.schemas.process.ps_node import Ps_NodeFrontResponse, Ps_NodeRequest, Ps_NodeDeleteRequest
|
||||
from api.schemas.process.ps_node import (
|
||||
Ps_NodeFrontResponse,
|
||||
Ps_NodeRequest,
|
||||
Ps_NodeDeleteRequest,
|
||||
AllAvailableNodesResponse,
|
||||
Ps_NodeUpdate,
|
||||
)
|
||||
from api.services.auth import get_current_user
|
||||
from api.db.logic.ps_node import get_ps_node_by_id, check_node_connection
|
||||
from api.db.logic.ps_node import get_ps_node_by_id
|
||||
from api.db.logic.process_schema import get_process_schema_by_id
|
||||
from api.services.user_role_validation import UserRoleValidator
|
||||
from core import VorkNodeRegistry
|
||||
@@ -21,6 +27,214 @@ api_router = APIRouter(
|
||||
)
|
||||
|
||||
|
||||
|
||||
@api_router.get("/available", dependencies=[Depends(bearer_schema)], response_model=AllAvailableNodesResponse)
|
||||
async def get_all_available_nodes_endpoint(
|
||||
current_user=Depends(get_current_user),
|
||||
):
|
||||
registry = VorkNodeRegistry()
|
||||
all_nodes = registry.get_all()
|
||||
node_ids = list(all_nodes.keys())
|
||||
|
||||
return AllAvailableNodesResponse(nodes=node_ids)
|
||||
|
||||
|
||||
@api_router.get("/{node_id}", dependencies=[Depends(bearer_schema)], response_model=Ps_NodeFrontResponse)
|
||||
async def get_ps_node_endpoint(
|
||||
node_id: int,
|
||||
connection: AsyncConnection = Depends(get_connection_dep),
|
||||
current_user=Depends(get_current_user),
|
||||
):
|
||||
service = PsNodeService(connection)
|
||||
ps_node_response = await service.get(node_id)
|
||||
|
||||
if ps_node_response is None:
|
||||
raise create_operation_error(
|
||||
message="PS node not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"node_id": node_id},
|
||||
)
|
||||
|
||||
process_schema = await get_process_schema_by_id(connection, ps_node_response.node.ps_id)
|
||||
if process_schema is None:
|
||||
raise create_operation_error(
|
||||
message="Process schema not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"schema_id": ps_node_response.node.ps_id},
|
||||
)
|
||||
|
||||
validator = UserRoleValidator(connection)
|
||||
try:
|
||||
await validator.validate_ownership(current_user, process_schema.creator_id)
|
||||
except Exception as e:
|
||||
raise create_access_error(
|
||||
message="Access denied",
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
details={"user_id": current_user, "schema_creator_id": process_schema.creator_id, "reason": str(e)},
|
||||
)
|
||||
|
||||
return ps_node_response
|
||||
|
||||
@api_router.post("", dependencies=[Depends(bearer_schema)], response_model=Ps_NodeFrontResponse)
|
||||
async def create_ps_node_endpoint(
|
||||
ps_node: Ps_NodeRequest,
|
||||
connection: AsyncConnection = Depends(get_connection_dep),
|
||||
current_user=Depends(get_current_user),
|
||||
):
|
||||
user_validation = await get_user_by_login(connection, current_user)
|
||||
|
||||
process_schema = await get_process_schema_by_id(connection, ps_node.data.ps_id)
|
||||
if process_schema is None:
|
||||
raise create_operation_error(
|
||||
message="Process schema not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"schema_id": ps_node.data.ps_id},
|
||||
)
|
||||
|
||||
validator = UserRoleValidator(connection)
|
||||
try:
|
||||
await validator.validate_ownership(current_user, process_schema.creator_id)
|
||||
except Exception as e:
|
||||
raise create_access_error(
|
||||
message="Access denied",
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
details={"user_id": current_user, "schema_creator_id": process_schema.creator_id, "reason": str(e)},
|
||||
)
|
||||
|
||||
registery = VorkNodeRegistry()
|
||||
|
||||
vork_node = registery.get(ps_node.data.node_type.value)
|
||||
|
||||
if vork_node is None:
|
||||
raise create_operation_error(
|
||||
message="Node type not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"node_type": ps_node.data.node_type},
|
||||
)
|
||||
|
||||
try:
|
||||
# Преобразуем объекты Pydantic в словари для vork_node
|
||||
node_instance = vork_node(data=ps_node.data.model_dump(), links=ps_node.links.model_dump())
|
||||
node_instance_validated = node_instance.validate()
|
||||
except Exception as e:
|
||||
raise create_validation_error(
|
||||
message="Node validation failed",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
details={"error": str(e)},
|
||||
)
|
||||
|
||||
parent_id = node_instance_validated.parent_id
|
||||
target_ps_id = ps_node.data.ps_id
|
||||
|
||||
parent_node = await get_ps_node_by_id(connection, parent_id)
|
||||
if parent_node is None:
|
||||
raise create_operation_error(
|
||||
message="Parent PS node not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"parent_id": parent_id},
|
||||
)
|
||||
if parent_node.ps_id != target_ps_id:
|
||||
raise create_validation_error(
|
||||
message="Parent PS node belongs to another process schema",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
details={"parent_id": parent_id, "expected_ps_id": target_ps_id, "actual_ps_id": parent_node.ps_id},
|
||||
)
|
||||
|
||||
service = PsNodeService(connection)
|
||||
ps_node_response = await service.create(ps_node.data.model_dump(), ps_node.links.model_dump(), user_validation.id)
|
||||
|
||||
return ps_node_response
|
||||
|
||||
@api_router.put("", dependencies=[Depends(bearer_schema)], response_model=Ps_NodeFrontResponse)
|
||||
async def update_ps_node_endpoint(
|
||||
ps_node_update: Ps_NodeUpdate,
|
||||
connection: AsyncConnection = Depends(get_connection_dep),
|
||||
current_user=Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Обновляет settings PS ноды.
|
||||
|
||||
"""
|
||||
|
||||
process_schema = await get_process_schema_by_id(connection, ps_node_update.schema_id)
|
||||
if process_schema is None:
|
||||
raise create_operation_error(
|
||||
message="Process schema not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"schema_id": ps_node_update.schema_id},
|
||||
)
|
||||
|
||||
validator = UserRoleValidator(connection)
|
||||
try:
|
||||
await validator.validate_ownership(current_user, process_schema.creator_id)
|
||||
except Exception as e:
|
||||
raise create_access_error(
|
||||
message="Access denied",
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
details={"user_id": current_user, "schema_creator_id": process_schema.creator_id, "reason": str(e)},
|
||||
)
|
||||
|
||||
ps_node = await get_ps_node_by_id(connection, ps_node_update.node_id)
|
||||
if ps_node is None:
|
||||
raise create_operation_error(
|
||||
message="PS node not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"node_id": ps_node_update.node_id},
|
||||
)
|
||||
|
||||
if ps_node.ps_id != ps_node_update.schema_id:
|
||||
raise create_validation_error(
|
||||
message="PS node does not belong to the specified schema",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
details={
|
||||
"node_id": ps_node_update.node_id,
|
||||
"expected_schema_id": ps_node_update.schema_id,
|
||||
"actual_schema_id": ps_node.ps_id,
|
||||
},
|
||||
)
|
||||
|
||||
# Получаем дескриптор формы для проверки constraint
|
||||
registry = VorkNodeRegistry()
|
||||
node_type_str = ps_node.node_type.value if hasattr(ps_node.node_type, "value") else str(ps_node.node_type)
|
||||
vork_node = registry.get(node_type_str)
|
||||
|
||||
if vork_node is None:
|
||||
raise create_operation_error(
|
||||
message="Node type not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"node_type": node_type_str},
|
||||
)
|
||||
|
||||
form_descriptor = vork_node.form()
|
||||
|
||||
if hasattr(form_descriptor, "model_dump"):
|
||||
form_descriptor_dict = form_descriptor.model_dump()
|
||||
else:
|
||||
form_descriptor_dict = form_descriptor
|
||||
|
||||
service = PsNodeService(connection)
|
||||
constraint_valid = service.check_constraint(form_descriptor_dict, ps_node_update.data)
|
||||
|
||||
if not constraint_valid:
|
||||
raise create_validation_error(
|
||||
message="Constraint validation failed",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
details={"node_id": ps_node_update.node_id},
|
||||
)
|
||||
|
||||
try:
|
||||
updated_ps_node = await service.update(ps_node_update.node_id, ps_node_update.data, ps_node)
|
||||
except Exception as e:
|
||||
raise create_server_error(
|
||||
message="Failed to update node",
|
||||
status_code=500,
|
||||
details={"error": str(e), "node_id": ps_node_update.node_id},
|
||||
)
|
||||
|
||||
return updated_ps_node
|
||||
|
||||
|
||||
|
||||
@api_router.delete("", dependencies=[Depends(bearer_schema)], status_code=status.HTTP_200_OK)
|
||||
async def delete_ps_node_endpoint(
|
||||
ps_node_delete_data: Ps_NodeDeleteRequest,
|
||||
@@ -72,80 +286,3 @@ async def delete_ps_node_endpoint(
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@api_router.post("", dependencies=[Depends(bearer_schema)], response_model=Ps_NodeFrontResponse)
|
||||
async def create_ps_node_endpoint(
|
||||
ps_node: Ps_NodeRequest,
|
||||
connection: AsyncConnection = Depends(get_connection_dep),
|
||||
current_user=Depends(get_current_user),
|
||||
):
|
||||
user_validation = await get_user_by_login(connection, current_user)
|
||||
|
||||
process_schema = await get_process_schema_by_id(connection, ps_node.data["ps_id"])
|
||||
if process_schema is None:
|
||||
raise create_operation_error(
|
||||
message="Process schema not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"schema_id": ps_node.data["ps_id"]},
|
||||
)
|
||||
|
||||
validator = UserRoleValidator(connection)
|
||||
try:
|
||||
await validator.validate_ownership(current_user, process_schema.creator_id)
|
||||
except Exception as e:
|
||||
raise create_access_error(
|
||||
message="Access denied",
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
details={"user_id": current_user, "schema_creator_id": process_schema.creator_id, "reason": str(e)},
|
||||
)
|
||||
|
||||
registery = VorkNodeRegistry()
|
||||
|
||||
vork_node = registery.get(ps_node.data["node_type"])
|
||||
|
||||
if vork_node is None:
|
||||
raise create_operation_error(
|
||||
message="Node type not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"node_type": ps_node.data["node_type"]},
|
||||
)
|
||||
|
||||
try:
|
||||
node_instance = vork_node(data=ps_node.data, links=ps_node.links)
|
||||
node_instance_validated = node_instance.validate()
|
||||
except Exception as e:
|
||||
raise create_validation_error(
|
||||
message="Node validation failed",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
details={"error": str(e)},
|
||||
)
|
||||
|
||||
parent_id = node_instance_validated.parent_id
|
||||
target_ps_id = ps_node.data["ps_id"]
|
||||
|
||||
parent_node = await get_ps_node_by_id(connection, parent_id)
|
||||
if parent_node is None:
|
||||
raise create_operation_error(
|
||||
message="Parent PS node not found",
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
details={"parent_id": parent_id},
|
||||
)
|
||||
if parent_node.ps_id != target_ps_id:
|
||||
raise create_validation_error(
|
||||
message="Parent PS node belongs to another process schema",
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
details={"parent_id": parent_id, "expected_ps_id": target_ps_id, "actual_ps_id": parent_node.ps_id},
|
||||
)
|
||||
|
||||
service = PsNodeService(connection)
|
||||
try:
|
||||
ps_node_front_response = await service.create(ps_node.data, ps_node.links, user_validation.id)
|
||||
except Exception as e:
|
||||
raise create_server_error(
|
||||
message="Failed to create node",
|
||||
status_code=500,
|
||||
details={"error": str(e)},
|
||||
)
|
||||
|
||||
return ps_node_front_response
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Any
|
||||
from orm.tables.process import NodeStatus, NodeType
|
||||
|
||||
from api.schemas.base import Base
|
||||
from api.schemas.process.node_link import NodeLink
|
||||
|
||||
|
||||
class Ps_NodeDeleteRequest(Base):
|
||||
@@ -12,10 +13,17 @@ class Ps_NodeDeleteRequest(Base):
|
||||
port: str
|
||||
next_node_id: int
|
||||
|
||||
class Ps_NodeRequestData(Base):
|
||||
ps_id: int
|
||||
node_type: NodeType
|
||||
|
||||
class Ps_NodeRequestLinks(Base):
|
||||
parent_port_number: int
|
||||
parent_id: int
|
||||
|
||||
class Ps_NodeRequest(Base):
|
||||
data: dict[str, Any]
|
||||
links: dict[str, Any]
|
||||
data: Ps_NodeRequestData
|
||||
links: Ps_NodeRequestLinks
|
||||
|
||||
|
||||
class Ps_Node(Base):
|
||||
@@ -28,6 +36,16 @@ class Ps_Node(Base):
|
||||
status: NodeStatus
|
||||
|
||||
|
||||
class Ps_NodeUpdate(Base):
|
||||
schema_id: int
|
||||
node_id: int
|
||||
data: dict[str, Any]
|
||||
|
||||
|
||||
class Ps_NodeFrontResponse(Base):
|
||||
node: dict[str, Any] | None = None
|
||||
link: list[dict[str, Any]] | None = None
|
||||
node: Ps_Node
|
||||
link: list[NodeLink] | None = None
|
||||
|
||||
|
||||
class AllAvailableNodesResponse(Base):
|
||||
nodes: list[str]
|
||||
|
||||
@@ -53,11 +53,11 @@ class ProcessSchemaService:
|
||||
nodes_response = []
|
||||
for node in nodes:
|
||||
node_links = links_by_node_id.get(node.id, [])
|
||||
links_list = [{"link": link.model_dump()} for link in node_links]
|
||||
link = node_links if node_links else None
|
||||
|
||||
ps_node_front_response = Ps_NodeFrontResponse(
|
||||
node=node.model_dump(),
|
||||
link=links_list,
|
||||
link=link,
|
||||
)
|
||||
nodes_response.append(ps_node_front_response)
|
||||
|
||||
@@ -78,7 +78,7 @@ class ProcessSchemaService:
|
||||
|
||||
process_schema_new = await get_process_schema_by_id(self.connection, node_id)
|
||||
|
||||
start_node_data = ListenNodeData(ps_id=process_schema_new.id, node_type=NodeType.START.value, is_start="True")
|
||||
start_node_data = ListenNodeData(ps_id=process_schema_new.id, node_type=NodeType.LISTEN.value, is_start="True")
|
||||
|
||||
start_node_links = {}
|
||||
|
||||
@@ -107,7 +107,7 @@ class ProcessSchemaService:
|
||||
|
||||
ps_node_front_response = Ps_NodeFrontResponse(
|
||||
node=db_start_schema.model_dump(),
|
||||
link=[],
|
||||
link=None,
|
||||
)
|
||||
response_data = {
|
||||
"process_schema": process_schema_new.model_dump(),
|
||||
|
||||
@@ -5,9 +5,15 @@ from api.db.logic.ps_node import (
|
||||
get_nodes_for_deletion_ordered,
|
||||
delete_ps_nodes_delete_handler,
|
||||
create_ps_node_schema,
|
||||
get_ps_node_by_id,
|
||||
)
|
||||
from api.db.logic.node_link import get_last_link_name_by_node_id, create_node_link_schema
|
||||
from api.schemas.process.ps_node import Ps_NodeFrontResponse
|
||||
from api.db.logic.node_link import (
|
||||
get_last_link_name_by_node_id,
|
||||
create_node_link_schema,
|
||||
get_all_node_links_by_next_node_ids,
|
||||
)
|
||||
from api.db.logic.ps_node import update_ps_node_by_id
|
||||
from api.schemas.process.ps_node import Ps_NodeFrontResponse, Ps_Node
|
||||
from core import VorkNodeRegistry, VorkNodeLink
|
||||
from model_nodes import VorkNodeLinkData
|
||||
|
||||
@@ -36,7 +42,10 @@ class PsNodeService:
|
||||
Создаёт новую ноду с линком и обновляет настройки схемы процесса.
|
||||
"""
|
||||
registery = VorkNodeRegistry()
|
||||
vork_node = registery.get(ps_node_data["node_type"])
|
||||
|
||||
node_type = ps_node_data.get("node_type")
|
||||
vork_node = registery.get(node_type.value)
|
||||
|
||||
node_descriptor = vork_node.form()
|
||||
|
||||
node_instance = vork_node(data=ps_node_data, links=links)
|
||||
@@ -58,8 +67,53 @@ class PsNodeService:
|
||||
db_node_link = await create_node_link_schema(self.connection, validated_link, creator_id)
|
||||
|
||||
ps_node_front_response = Ps_NodeFrontResponse(
|
||||
node=db_ps_node.model_dump(),
|
||||
link=[{"link": db_node_link.model_dump()}],
|
||||
node=db_ps_node,
|
||||
link=[db_node_link] if db_node_link else None,
|
||||
)
|
||||
|
||||
return ps_node_front_response
|
||||
|
||||
async def get(self, node_id: int) -> Ps_NodeFrontResponse | None:
|
||||
"""
|
||||
Получает конкретную ноду по ID с её вышестоящими линками.
|
||||
"""
|
||||
ps_node = await get_ps_node_by_id(self.connection, node_id)
|
||||
if ps_node is None:
|
||||
return None
|
||||
|
||||
node_links = await get_all_node_links_by_next_node_ids(self.connection, [node_id])
|
||||
|
||||
return Ps_NodeFrontResponse(
|
||||
node=ps_node,
|
||||
link=node_links if node_links else None,
|
||||
)
|
||||
|
||||
async def update(self, node_id: int, update_data: dict[str, Any], ps_node: Ps_Node) -> Ps_NodeFrontResponse:
|
||||
"""
|
||||
Обновляет данные PS ноды (settings).
|
||||
Принимает уже полученную ноду, чтобы избежать лишних запросов к БД.
|
||||
"""
|
||||
await update_ps_node_by_id(self.connection, {"settings": update_data}, ps_node)
|
||||
|
||||
node_links = await get_all_node_links_by_next_node_ids(self.connection, [node_id])
|
||||
|
||||
# Создаем обновленный объект Ps_Node с новыми settings
|
||||
updated_node_dict = ps_node.model_dump()
|
||||
updated_node_dict["settings"] = update_data
|
||||
updated_ps_node = Ps_Node.model_validate(updated_node_dict)
|
||||
|
||||
return Ps_NodeFrontResponse(
|
||||
node=updated_ps_node,
|
||||
link=node_links if node_links else None,
|
||||
)
|
||||
|
||||
def check_constraint(self, form_descriptor: dict[str, Any], settings_data: dict[str, Any]) -> bool:
|
||||
"""
|
||||
Валидирует значения инпутов по constraint .
|
||||
|
||||
На данный момент это заглушка, всегда возвращающая True,
|
||||
так как в дескрипторе пока нет constraint.
|
||||
|
||||
"""
|
||||
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user