From c5d29cced84374985a222722e05aa4d20e3d0a27 Mon Sep 17 00:00:00 2001 From: Zachary Hamm Date: Wed, 20 Nov 2024 12:03:59 -0600 Subject: [PATCH] fix: copy management edges when pasting components --- lib/sdf-server/src/service/diagram.rs | 2 - .../src/service/diagram/paste_component.rs | 176 ------------------ .../src/service/v2/view/paste_component.rs | 54 ++++-- 3 files changed, 41 insertions(+), 191 deletions(-) delete mode 100644 lib/sdf-server/src/service/diagram/paste_component.rs diff --git a/lib/sdf-server/src/service/diagram.rs b/lib/sdf-server/src/service/diagram.rs index 8664d104e0..aabd3f14bd 100644 --- a/lib/sdf-server/src/service/diagram.rs +++ b/lib/sdf-server/src/service/diagram.rs @@ -31,7 +31,6 @@ pub mod set_component_position; pub mod delete_component; pub mod delete_connection; -pub mod paste_component; pub mod remove_delete_intent; mod add_components_to_view; @@ -155,7 +154,6 @@ impl IntoResponse for DiagramError { pub fn routes() -> Router { Router::new() - .route("/paste_components", post(paste_component::paste_components)) .route( "/add_components_to_view", post(add_components_to_view::add_components_to_view), diff --git a/lib/sdf-server/src/service/diagram/paste_component.rs b/lib/sdf-server/src/service/diagram/paste_component.rs deleted file mode 100644 index 77cbe3d23c..0000000000 --- a/lib/sdf-server/src/service/diagram/paste_component.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::collections::HashMap; - -use super::{DiagramError, DiagramResult}; -use crate::{ - extract::{AccessBuilder, HandlerContext, PosthogClient}, - service::force_change_set_response::ForceChangeSetResponse, - track, -}; -use axum::{ - extract::{Host, OriginalUri}, - http::uri::Uri, - Json, -}; -use dal::diagram::view::{View, ViewId}; -use dal::{ - change_status::ChangeStatus, component::frame::Frame, diagram::SummaryDiagramEdge, ChangeSet, - Component, ComponentId, DalContext, Visibility, WsEvent, -}; -use serde::{Deserialize, Serialize}; -use si_frontend_types::RawGeometry; - -#[allow(clippy::too_many_arguments)] -async fn paste_single_component( - ctx: &DalContext, - component_id: ComponentId, - component_geometry: RawGeometry, - original_uri: &Uri, - host_name: &String, - PosthogClient(posthog_client): &PosthogClient, - view_id: ViewId, -) -> DiagramResult { - let original_comp = Component::get_by_id(ctx, component_id).await?; - let pasted_comp = original_comp - .create_copy(ctx, view_id, component_geometry) - .await?; - - let schema = pasted_comp.schema(ctx).await?; - track( - posthog_client, - ctx, - original_uri, - host_name, - "paste_component", - serde_json::json!({ - "how": "/diagram/paste_component", - "component_id": pasted_comp.id(), - "component_schema_name": schema.name(), - }), - ); - - Ok(pasted_comp) -} - -#[derive(Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct PasteSingleComponentPayload { - id: ComponentId, - component_geometry: RawGeometry, -} - -#[derive(Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct PasteComponentsRequest { - pub components: Vec, - pub new_parent_node_id: Option, - #[serde(flatten)] - pub visibility: Visibility, -} - -/// Paste a set of [`Component`](dal::Component)s via their componentId. Creates change-set if on head -pub async fn paste_components( - HandlerContext(builder): HandlerContext, - AccessBuilder(request_ctx): AccessBuilder, - PosthogClient(posthog_client): PosthogClient, - OriginalUri(original_uri): OriginalUri, - Host(host_name): Host, - Json(request): Json, -) -> DiagramResult> { - let mut ctx = builder.build(request_ctx.build(request.visibility)).await?; - - let force_change_set_id = ChangeSet::force_new(&mut ctx).await?; - - let view_id = View::get_id_for_default(&ctx).await?; - - let mut pasted_components_by_original = HashMap::new(); - for component_payload in &request.components { - let component_id = component_payload.id; - - let posthog_client = PosthogClient(posthog_client.clone()); - let pasted_comp = paste_single_component( - &ctx, - component_id, - component_payload.component_geometry.clone(), - &original_uri, - &host_name, - &posthog_client, - view_id, - ) - .await?; - - pasted_components_by_original.insert(component_id, pasted_comp); - } - - for component_payload in &request.components { - let component_id = component_payload.id; - - let pasted_component = - if let Some(component) = pasted_components_by_original.get(&component_id) { - component - } else { - return Err(DiagramError::Paste); - }; - let component = Component::get_by_id(&ctx, component_id).await?; - - // If component parent was also pasted on this batch, keep relationship between new components - if let Some(parent_id) = component.parent(&ctx).await? { - if let Some(pasted_parent) = pasted_components_by_original.get(&parent_id) { - Frame::upsert_parent(&ctx, pasted_component.id(), pasted_parent.id()).await?; - }; - } - - // If the pasted component didn't get a parent already, set the new parent - if pasted_component.parent(&ctx).await?.is_none() { - if let Some(parent_id) = request.new_parent_node_id { - Frame::upsert_parent(&ctx, pasted_component.id(), parent_id).await?; - } - } - - // re-fetch component with possible parentage - let pasted_component = Component::get_by_id(&ctx, pasted_component.id()).await?; - let mut diagram_sockets = HashMap::new(); - let payload = pasted_component - .into_frontend_type_for_default_view(&ctx, ChangeStatus::Added, &mut diagram_sockets) - .await?; - WsEvent::component_created(&ctx, payload) - .await? - .publish_on_commit(&ctx) - .await?; - - // Create on pasted components copies of edges that existed between original components - for connection in component.incoming_connections(&ctx).await? { - if let Some(from_component) = - pasted_components_by_original.get(&connection.from_component_id) - { - Component::connect( - &ctx, - from_component.id(), - connection.from_output_socket_id, - pasted_component.id(), - connection.to_input_socket_id, - ) - .await?; - - let edge = SummaryDiagramEdge { - from_component_id: from_component.id(), - from_socket_id: connection.from_output_socket_id, - to_component_id: pasted_component.id(), - to_socket_id: connection.to_input_socket_id, - change_status: ChangeStatus::Added, - created_info: serde_json::to_value(connection.created_info)?, - deleted_info: serde_json::to_value(connection.deleted_info)?, - to_delete: false, - from_base_change_set: false, - }; - WsEvent::connection_upserted(&ctx, edge.into()) - .await? - .publish_on_commit(&ctx) - .await?; - } - } - } - - ctx.commit().await?; - - Ok(ForceChangeSetResponse::empty(force_change_set_id)) -} diff --git a/lib/sdf-server/src/service/v2/view/paste_component.rs b/lib/sdf-server/src/service/v2/view/paste_component.rs index 7f7d1ef265..3b2756e3ac 100644 --- a/lib/sdf-server/src/service/v2/view/paste_component.rs +++ b/lib/sdf-server/src/service/v2/view/paste_component.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use telemetry::prelude::*; use super::{ViewError, ViewResult}; use crate::{ @@ -70,7 +71,7 @@ pub async fn paste_component( &host_name, "paste_component", serde_json::json!({ - "how": "/diagram/paste_component", + "how": "/v2/view/paste_component", "component_id": pasted_comp.id(), "component_schema_name": schema.name(), }), @@ -82,19 +83,19 @@ pub async fn paste_component( for component_payload in &request.components { let component_id = component_payload.id; - let pasted_component = - if let Some(component) = pasted_components_by_original.get(&component_id) { - component - } else { - return Err(ViewError::Paste); - }; - let component = Component::get_by_id(&ctx, component_id).await?; + let pasted_component = pasted_components_by_original + .get(&component_id) + .ok_or(ViewError::Paste)?; + let original_component = Component::get_by_id(&ctx, component_id).await?; // If component parent was also pasted on this batch, keep relationship between new components - if let Some(parent_id) = component.parent(&ctx).await? { - if let Some(pasted_parent) = pasted_components_by_original.get(&parent_id) { - Frame::upsert_parent(&ctx, pasted_component.id(), pasted_parent.id()).await?; - }; + + if let Some(pasted_parent) = original_component + .parent(&ctx) + .await? + .and_then(|parent_id| pasted_components_by_original.get(&parent_id)) + { + Frame::upsert_parent(&ctx, pasted_component.id(), pasted_parent.id()).await?; } // If the pasted component didn't get a parent already, set the new parent @@ -115,8 +116,35 @@ pub async fn paste_component( .publish_on_commit(&ctx) .await?; + for original_manager_id in original_component.managers(&ctx).await? { + let Some(pasted_manager) = pasted_components_by_original.get(&original_manager_id) + else { + continue; + }; + + match Component::manage_component(&ctx, pasted_manager.id(), pasted_component.id()) + .await + { + Ok(edge) => { + WsEvent::connection_upserted(&ctx, edge.into()) + .await? + .publish_on_commit(&ctx) + .await?; + } + Err(dal::ComponentError::ComponentNotManagedSchema(_, _, _)) => { + // This error should not occur, but we also don't want to + // fail the paste just because the managed schemas are out + // of sync + error!("Could not manage pasted component, but continuing paste"); + } + Err(err) => { + return Err(err)?; + } + }; + } + // Create on pasted components copies of edges that existed between original components - for connection in component.incoming_connections(&ctx).await? { + for connection in original_component.incoming_connections(&ctx).await? { if let Some(from_component) = pasted_components_by_original.get(&connection.from_component_id) {