Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions backend/app/api/v1/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

from app.api.deps import get_current_workspace_id, get_optional_user
from app.core.database import get_db
from app.schemas.connection import ConnectionCreate, ConnectionResponse, ConnectionUpdate
from app.realtime.manager import (
fire_and_forget_publish,
fire_and_forget_publish_diagram,
)
from app.schemas.connection import ConnectionCreate, ConnectionResponse, ConnectionUpdate
from app.services import connection_service, diagram_service, object_service
from app.services.webhook_service import fire_and_forget_emit

Expand Down Expand Up @@ -84,8 +84,9 @@ async def create_connection(
from_diagram_id=data.from_diagram_id,
from_draft_id=data.from_draft_id,
)
body = ConnectionResponse.model_validate(conn).model_dump(mode="json")
await db.commit()
if draft_id is None:
body = ConnectionResponse.model_validate(conn).model_dump(mode="json")
fire_and_forget_emit("connection.created", body)
fire_and_forget_publish(
getattr(source, "workspace_id", None),
Expand Down Expand Up @@ -115,8 +116,9 @@ async def update_connection(
from_diagram_id=data.from_diagram_id,
from_draft_id=data.from_draft_id,
)
body = ConnectionResponse.model_validate(conn).model_dump(mode="json")
await db.commit()
if conn.draft_id is None:
body = ConnectionResponse.model_validate(conn).model_dump(mode="json")
fire_and_forget_emit("connection.updated", body)
src = await object_service.get_object(db, conn.source_id)
fire_and_forget_publish(
Expand Down Expand Up @@ -148,8 +150,9 @@ async def flip_connection(
from_diagram_id=from_diagram_id,
from_draft_id=from_draft_id,
)
body = ConnectionResponse.model_validate(conn).model_dump(mode="json")
await db.commit()
if conn.draft_id is None:
body = ConnectionResponse.model_validate(conn).model_dump(mode="json")
fire_and_forget_emit("connection.updated", body)
src = await object_service.get_object(db, conn.source_id)
fire_and_forget_publish(
Expand Down Expand Up @@ -187,6 +190,7 @@ async def delete_connection(
from_diagram_id=from_diagram_id,
from_draft_id=from_draft_id,
)
await db.commit()
if not was_draft:
fire_and_forget_emit("connection.deleted", {"id": conn_id_str})
fire_and_forget_publish(src_ws_id, "connection.deleted", {"id": conn_id_str})
Expand Down
35 changes: 23 additions & 12 deletions backend/app/api/v1/diagrams.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_current_workspace_id, get_optional_user
from app.core.database import get_db
from app.realtime.manager import (
fire_and_forget_publish,
fire_and_forget_publish_diagram,
)
from app.schemas.diagram import (
DiagramCreate,
DiagramObjectCreate,
Expand All @@ -13,13 +18,13 @@
DiagramResponse,
DiagramUpdate,
)
from app.api.deps import get_current_workspace_id, get_optional_user
from app.realtime.manager import (
fire_and_forget_publish,
fire_and_forget_publish_diagram,
from app.services import (
access_service,
diagram_service,
draft_service,
pack_service,
workspace_service,
)
from app.services import access_service, diagram_service, draft_service, workspace_service
from app.services import pack_service
from app.services.webhook_service import fire_and_forget_emit

router = APIRouter(prefix="/diagrams", tags=["diagrams"])
Expand Down Expand Up @@ -177,14 +182,18 @@ async def add_object_to_diagram(
diagram = await diagram_service.get_diagram(db, diagram_id)
if not diagram:
raise HTTPException(status_code=404, detail="Diagram not found")
obj = await diagram_service.add_object_to_diagram(
db, diagram_id, data,
actor_user=current_user,
workspace_id=getattr(diagram, "workspace_id", None),
from_draft_id=data.from_draft_id,
)
try:
obj = await diagram_service.add_object_to_diagram(
db, diagram_id, data,
actor_user=current_user,
workspace_id=getattr(diagram, "workspace_id", None),
from_draft_id=data.from_draft_id,
)
except diagram_service.DiagramObjectTargetMissingError as exc:
raise HTTPException(status_code=404, detail="Object not found") from exc
body = DiagramObjectResponse.model_validate(obj).model_dump(mode="json")
payload = {"diagram_id": str(diagram_id), "diagram_object": body}
await db.commit()
fire_and_forget_publish(
getattr(diagram, "workspace_id", None),
"diagram_object.added",
Expand Down Expand Up @@ -219,6 +228,7 @@ async def update_diagram_object(
)
body = DiagramObjectResponse.model_validate(obj).model_dump(mode="json")
payload = {"diagram_id": str(diagram_id), "diagram_object": body}
await db.commit()
fire_and_forget_publish(
getattr(diagram, "workspace_id", None) if diagram else None,
"diagram_object.updated",
Expand Down Expand Up @@ -249,6 +259,7 @@ async def remove_object_from_diagram(
status_code=404, detail="Object not found in diagram"
)
payload = {"diagram_id": str(diagram_id), "object_id": str(object_id)}
await db.commit()
fire_and_forget_publish(
getattr(diagram, "workspace_id", None) if diagram else None,
"diagram_object.removed",
Expand Down
1 change: 1 addition & 0 deletions backend/app/api/v1/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ async def create_object(
detail={"error": "invalid_repo_url", "message": str(exc)},
) from exc
response = ObjectResponse.from_model(obj)
await db.commit()
if draft_id is None:
body = response.model_dump(mode="json")
fire_and_forget_emit("object.created", body)
Expand Down
20 changes: 15 additions & 5 deletions backend/app/services/diagram_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from app.models.connection import Connection
from app.models.diagram import Diagram, DiagramObject
from app.models.object import ModelObject
from app.models.technology import Technology
from app.schemas.diagram import (
DiagramCreate,
Expand Down Expand Up @@ -140,6 +141,11 @@ async def delete_diagram(db: AsyncSession, diagram: Diagram) -> None:

# ─── Diagram Objects (positions) ──────────────────────────


class DiagramObjectTargetMissingError(ValueError):
"""The object being placed on a diagram does not exist."""


async def get_diagram_objects(
db: AsyncSession, diagram_id: uuid.UUID
) -> list[DiagramObject]:
Expand Down Expand Up @@ -170,6 +176,10 @@ async def add_object_to_diagram(
workspace_id: uuid.UUID | None = None,
from_draft_id: uuid.UUID | None = None,
) -> DiagramObject:
target = await db.get(ModelObject, data.object_id)
if target is None:
raise DiagramObjectTargetMissingError(str(data.object_id))

obj = DiagramObject(
diagram_id=diagram_id,
object_id=data.object_id,
Expand Down Expand Up @@ -199,7 +209,7 @@ async def add_object_to_diagram(
target_type=UndoTargetType.DIAGRAM_OBJECT,
target_id=obj.id,
action=UndoAction.CREATE,
forward_summary=f"Added object to diagram"[:80],
forward_summary="Added object to diagram"[:80],
inverse_payload={"target_id": str(obj.id)},
after_state=activity_service.snapshot(obj, include_metadata=True),
coalesce_key=f"diagram_object:{obj.id}:create",
Expand Down Expand Up @@ -263,7 +273,7 @@ async def update_diagram_object(
target_type=UndoTargetType.DIAGRAM_OBJECT,
target_id=obj.id,
action=UndoAction.UPDATE,
forward_summary=f"Moved object in diagram"[:80],
forward_summary="Moved object in diagram"[:80],
inverse_payload={"before": {k: v["before"] for k, v in pos_diff.items()}},
after_state={k: v["after"] for k, v in pos_diff.items()},
coalesce_key=f"diagram_object:{obj.id}:position",
Expand All @@ -280,7 +290,7 @@ async def update_diagram_object(
target_type=UndoTargetType.DIAGRAM_OBJECT,
target_id=obj.id,
action=UndoAction.UPDATE,
forward_summary=f"Resized object in diagram"[:80],
forward_summary="Resized object in diagram"[:80],
inverse_payload={"before": {k: v["before"] for k, v in size_diff.items()}},
after_state={k: v["after"] for k, v in size_diff.items()},
coalesce_key=f"diagram_object:{obj.id}:size",
Expand All @@ -298,7 +308,7 @@ async def update_diagram_object(
target_type=UndoTargetType.DIAGRAM_OBJECT,
target_id=obj.id,
action=UndoAction.UPDATE,
forward_summary=f"Updated diagram object"[:80],
forward_summary="Updated diagram object"[:80],
inverse_payload={"before": {k: v["before"] for k, v in other_diff.items()}},
after_state={k: v["after"] for k, v in other_diff.items()},
coalesce_key=f"diagram_object:{obj.id}:{','.join(sorted(other_diff.keys()))}",
Expand Down Expand Up @@ -351,7 +361,7 @@ async def remove_object_from_diagram(
target_type=UndoTargetType.DIAGRAM_OBJECT,
target_id=do_id,
action=UndoAction.DELETE,
forward_summary=f"Removed object from diagram"[:80],
forward_summary="Removed object from diagram"[:80],
inverse_payload={"snapshot": snapshot, "id": str(do_id)},
after_state=None,
coalesce_key=f"diagram_object:{do_id}:delete",
Expand Down
Loading
Loading