diff --git a/lib/dal-test/src/lib.rs b/lib/dal-test/src/lib.rs index 0a8ccfde72..6788bf79e2 100644 --- a/lib/dal-test/src/lib.rs +++ b/lib/dal-test/src/lib.rs @@ -814,6 +814,7 @@ async fn migrate_local_builtins( schemas::migrate_test_exclusive_schema_bethesda_secret(&ctx).await?; schemas::migrate_test_exclusive_schema_swifty(&ctx).await?; schemas::migrate_test_exclusive_schema_katy_perry(&ctx).await?; + schemas::migrate_test_exclusive_schema_pirate(&ctx).await?; ctx.blocking_commit().await?; diff --git a/lib/dal-test/src/schemas/mod.rs b/lib/dal-test/src/schemas/mod.rs index 2ed00cd1f0..5443c05979 100644 --- a/lib/dal-test/src/schemas/mod.rs +++ b/lib/dal-test/src/schemas/mod.rs @@ -1,12 +1,14 @@ +pub use test_exclusive_schema_bethesda_secret::migrate_test_exclusive_schema_bethesda_secret; +pub use test_exclusive_schema_fallout::migrate_test_exclusive_schema_fallout; +pub use test_exclusive_schema_katy_perry::migrate_test_exclusive_schema_katy_perry; +pub use test_exclusive_schema_pirate::migrate_test_exclusive_schema_pirate; +pub use test_exclusive_schema_starfield::migrate_test_exclusive_schema_starfield; +pub use test_exclusive_schema_swifty::migrate_test_exclusive_schema_swifty; + mod schema_helpers; mod test_exclusive_schema_bethesda_secret; mod test_exclusive_schema_fallout; mod test_exclusive_schema_katy_perry; +mod test_exclusive_schema_pirate; mod test_exclusive_schema_starfield; mod test_exclusive_schema_swifty; - -pub use test_exclusive_schema_bethesda_secret::migrate_test_exclusive_schema_bethesda_secret; -pub use test_exclusive_schema_fallout::migrate_test_exclusive_schema_fallout; -pub use test_exclusive_schema_katy_perry::migrate_test_exclusive_schema_katy_perry; -pub use test_exclusive_schema_starfield::migrate_test_exclusive_schema_starfield; -pub use test_exclusive_schema_swifty::migrate_test_exclusive_schema_swifty; diff --git a/lib/dal-test/src/schemas/test_exclusive_schema_pirate.rs b/lib/dal-test/src/schemas/test_exclusive_schema_pirate.rs new file mode 100644 index 0000000000..1245a886b3 --- /dev/null +++ b/lib/dal-test/src/schemas/test_exclusive_schema_pirate.rs @@ -0,0 +1,108 @@ +use crate::schemas::schema_helpers::{build_asset_func, create_identity_func}; +use dal::pkg::import_pkg_from_pkg; +use dal::{pkg, prop::PropPath, ComponentType}; +use dal::{BuiltinsResult, DalContext, PropKind}; +use si_pkg::SchemaSpecData; +use si_pkg::{ + AttrFuncInputSpec, AttrFuncInputSpecKind, PkgSpec, PropSpec, SchemaSpec, SchemaVariantSpec, + SchemaVariantSpecData, SiPkg, +}; + +pub async fn migrate_test_exclusive_schema_pirate(ctx: &DalContext) -> BuiltinsResult<()> { + let mut builder = PkgSpec::builder(); + + builder + .name("pirate") + .version("2024-03-12") + .created_by("System Initiative"); + + let identity_func_spec = create_identity_func(); + + // Create Scaffold Func + let fn_name = "test:scaffoldPirateAsset"; + let authoring_schema_func = build_asset_func(fn_name).await?; + + let schema = SchemaSpec::builder() + .name("pirate") + .data( + SchemaSpecData::builder() + .name("pirate") + .category("test exclusive") + .category_name("pirate") + .build() + .expect("schema spec data build"), + ) + .variant( + SchemaVariantSpec::builder() + .name("v0") + .unique_id("pirate_sv") + .data( + SchemaVariantSpecData::builder() + .name("v0") + .color("#ff00ff") + .func_unique_id(&authoring_schema_func.unique_id) + .component_type(ComponentType::Component) + .build() + .expect("build variant spec data"), + ) + .domain_prop( + PropSpec::builder() + .name("name") + .kind(PropKind::String) + .func_unique_id(&identity_func_spec.unique_id) + .input( + AttrFuncInputSpec::builder() + .kind(AttrFuncInputSpecKind::Prop) + .name("identity") + .prop_path(PropPath::new(["root", "si", "name"])) + .build()?, + ) + .build()?, + ) + .domain_prop( + PropSpec::builder() + .name("parrot_names") + .kind(PropKind::Array) + .type_prop( + PropSpec::builder() + .kind(PropKind::String) + .name("parrot_name") + .build()?, + ) + .build()?, + ) + .domain_prop( + PropSpec::builder() + .name("treasure") + .kind(PropKind::Map) + .type_prop( + PropSpec::builder() + .kind(PropKind::String) + .name("location") + .build()?, + ) + .build()?, + ) + .build()?, + ) + .build()?; + + let spec = builder + .func(identity_func_spec) + .func(authoring_schema_func) + .schema(schema) + .build()?; + + let pkg = SiPkg::load_from_spec(spec)?; + import_pkg_from_pkg( + ctx, + &pkg, + Some(pkg::ImportOptions { + schemas: Some(vec!["pirate".into()]), + ..Default::default() + }), + ) + .await?; + + Ok(()) +} diff --git a/lib/dal/src/attribute/value.rs b/lib/dal/src/attribute/value.rs index cd98aa9609..c74a1eba99 100644 --- a/lib/dal/src/attribute/value.rs +++ b/lib/dal/src/attribute/value.rs @@ -147,6 +147,8 @@ pub enum AttributeValueError { NodeWeight(#[from] NodeWeightError), #[error("node weight mismatch, expected {0:?} to be {1:?}")] NodeWeightMismatch(NodeIndex, NodeWeightDiscriminants), + #[error("attribute value does not have ordering node as expected: {0}")] + NoOrderingNodeForAttributeValue(AttributeValueId), #[error("attribute value not found for component ({0}) and input socket ({1})")] NotFoundForComponentAndInputSocket(ComponentId, InputSocketId), #[error("attribute value {0} has no outgoing edge to a prop or socket")] @@ -159,6 +161,8 @@ pub enum AttributeValueError { PropMoreThanOneChild(PropId), #[error("prop not found for attribute value: {0}")] PropNotFound(AttributeValueId), + #[error("trying to delete av that's not related to child of map or array: {0}")] + RemovingWhenNotChildOrMapOrArray(AttributeValueId), #[error("serde_json: {0}")] SerdeJson(#[from] serde_json::Error), #[error("store error: {0}")] @@ -1941,4 +1945,102 @@ impl AttributeValue { None => None, }) } + + pub async fn get_parent_av_id_for_ordered_child( + ctx: &DalContext, + id: AttributeValueId, + ) -> AttributeValueResult> { + let workspace_snapshot = ctx.workspace_snapshot()?; + + let ordering_node_id = match workspace_snapshot + .incoming_sources_for_edge_weight_kind(id, EdgeWeightKindDiscriminants::Ordinal) + .await? + .first() + .copied() + { + Some(ordering_idx) => workspace_snapshot.get_node_weight(ordering_idx).await?.id(), + None => return Ok(None), + }; + + let parent_av_id = if let Some(parent_av_idx) = workspace_snapshot + .incoming_sources_for_edge_weight_kind( + ordering_node_id, + EdgeWeightKindDiscriminants::Ordering, + ) + .await? + .first() + .copied() + { + let parent_av_id: AttributeValueId = workspace_snapshot + .get_node_weight(parent_av_idx) + .await? + .id() + .into(); + + let prop_id = AttributeValue::prop(ctx, parent_av_id).await?; + + let parent_prop = Prop::get_by_id(ctx, prop_id).await?; + + if ![PropKind::Map, PropKind::Array].contains(&parent_prop.kind) { + return Ok(None); + } + + parent_av_id + } else { + return Ok(None); + }; + + Ok(Some(parent_av_id)) + } + + pub async fn get_child_av_ids_for_ordered_parent( + ctx: &DalContext, + id: AttributeValueId, + ) -> AttributeValueResult> { + let workspace_snapshot = ctx.workspace_snapshot()?; + + if let Some(ordering) = workspace_snapshot + .outgoing_targets_for_edge_weight_kind(id, EdgeWeightKindDiscriminants::Ordering) + .await? + .pop() + { + let node_weight = workspace_snapshot.get_node_weight(ordering).await?; + if let NodeWeight::Ordering(ordering_weight) = node_weight { + Ok(ordering_weight + .order() + .clone() + .into_iter() + .map(|ulid| ulid.into()) + .collect()) + } else { + Err(AttributeValueError::NodeWeightMismatch( + ordering, + NodeWeightDiscriminants::Ordering, + )) + } + } else { + Err(AttributeValueError::NoOrderingNodeForAttributeValue(id)) + } + } + + pub async fn remove_by_id(ctx: &DalContext, id: AttributeValueId) -> AttributeValueResult<()> { + let parent_av_id = Self::get_parent_av_id_for_ordered_child(ctx, id) + .await? + .ok_or(AttributeValueError::RemovingWhenNotChildOrMapOrArray(id))?; + + let av = Self::get_by_id(ctx, id).await?; + + ctx.workspace_snapshot()? + .remove_node_by_id(ctx.change_set_pointer()?, av.id) + .await?; + + ctx.enqueue_job(DependentValuesUpdate::new( + ctx.access_builder(), + *ctx.visibility(), + vec![parent_av_id], + )) + .await?; + + Ok(()) + } } diff --git a/lib/dal/src/property_editor/values.rs b/lib/dal/src/property_editor/values.rs index 1e69452155..6f4ae7be0f 100644 --- a/lib/dal/src/property_editor/values.rs +++ b/lib/dal/src/property_editor/values.rs @@ -49,7 +49,6 @@ impl PropertyEditorValues { .value(ctx) .await? .unwrap_or(Value::Null), - // TODO(nick): restore all these fields below. is_from_external_source: false, can_be_set_by_socket: false, is_controlled_by_intrinsic_func: true, @@ -66,28 +65,49 @@ impl PropertyEditorValues { // Collect all child attribute values. let mut cache: Vec<(AttributeValueId, Option)> = Vec::new(); { - let child_attribute_values_with_keys: Vec<(NodeIndex, Option)> = - workspace_snapshot - .edges_directed(attribute_value_id, Direction::Outgoing) - .await? - .into_iter() - .filter_map(|(edge_weight, _, target_idx)| { - if let EdgeWeightKind::Contain(key) = edge_weight.kind() { - Some((target_idx, key.to_owned())) - } else { - None - } - }) - .collect(); + let mut child_attribute_values_with_keys_by_id: HashMap< + AttributeValueId, + (NodeIndex, Option), + > = HashMap::new(); + + for (edge_weight, _, target_idx) in workspace_snapshot + .edges_directed(attribute_value_id, Direction::Outgoing) + .await? + { + if let EdgeWeightKind::Contain(key) = edge_weight.kind() { + let child_id = workspace_snapshot + .get_node_weight(target_idx) + .await? + .id() + .into(); + + child_attribute_values_with_keys_by_id + .insert(child_id, (target_idx, key.to_owned())); + } + } + + let maybe_ordering = + AttributeValue::get_child_av_ids_for_ordered_parent(ctx, attribute_value_id) + .await + .ok(); - // NOTE(nick): this entire function is likely wasteful. Zack and Jacob, have mercy on me. - for (child_attribute_value_node_index, key) in child_attribute_values_with_keys { + // Ideally every attribute value with children is connected via an ordering node + // We don't error out on ordering not existing here because we don't have that + // guarantee. If that becomes a certainty we should fail on maybe_ordering==None. + for av_id in maybe_ordering.unwrap_or_else(|| { + child_attribute_values_with_keys_by_id + .keys() + .cloned() + .collect() + }) { + let (child_attribute_value_node_index, key) = + &child_attribute_values_with_keys_by_id[&av_id]; let child_attribute_value_node_weight = workspace_snapshot - .get_node_weight(child_attribute_value_node_index) + .get_node_weight(*child_attribute_value_node_index) .await?; let content = child_attribute_value_node_weight.get_attribute_value_node_weight()?; - cache.push((content.id().into(), key)); + cache.push((content.id().into(), key.clone())); } } diff --git a/lib/dal/tests/integration_test/change_set.rs b/lib/dal/tests/integration_test/change_set.rs index 54cc9a7d5c..7b7c67d729 100644 --- a/lib/dal/tests/integration_test/change_set.rs +++ b/lib/dal/tests/integration_test/change_set.rs @@ -1,6 +1,6 @@ use dal::change_set_pointer::view::OpenChangeSetsView; use dal::change_set_pointer::ChangeSetPointer; -use dal::DalContext; +use dal::{ChangeSetStatus, DalContext}; use dal_test::test; use pretty_assertions_sorted::assert_eq; use std::collections::HashSet; @@ -118,3 +118,50 @@ async fn open_change_sets(ctx: &mut DalContext) { change_set_ids_again, // actual ); } + +#[test] +async fn abandon_change_set(ctx: &mut DalContext) { + let change_set_name = "for abandonment".to_string(); + let mut abandonment_change_set = ChangeSetPointer::fork_head(ctx, change_set_name.clone()) + .await + .expect("could not create new change set"); + ctx.update_visibility_and_snapshot_to_visibility(abandonment_change_set.id) + .await + .expect("could not update visibility"); + ctx.commit_no_rebase() + .await + .expect("could not perform commit"); + + // List open changesets. + let view = OpenChangeSetsView::assemble(ctx) + .await + .expect("could not assemble view"); + + // Check that the expected number of change sets exist.... + assert_eq!( + 3, // expected + view.change_sets.len() // actual + ); + + ctx.update_visibility_and_snapshot_to_visibility_no_editing_change_set(&abandonment_change_set) + .await + .expect("could not update visibility"); + abandonment_change_set + .update_status(ctx, ChangeSetStatus::Abandoned) + .await + .expect("Unable to abandon changeset"); + + // relist the open changesets. + let view = OpenChangeSetsView::assemble(ctx) + .await + .expect("could not assemble view"); + + // Check that we no longer have the abandoned changeset + assert_eq!( + 2, // expected + view.change_sets.len() // actual + ); + + let change_set_names = Vec::from_iter(view.change_sets.iter().map(|c| c.name.clone())); + assert!(!change_set_names.contains(&change_set_name)) +} diff --git a/lib/dal/tests/integration_test/property_editor.rs b/lib/dal/tests/integration_test/property_editor.rs index 0bedcff04c..f41d112a64 100644 --- a/lib/dal/tests/integration_test/property_editor.rs +++ b/lib/dal/tests/integration_test/property_editor.rs @@ -1,7 +1,8 @@ use dal::property_editor::schema::PropertyEditorSchema; use dal::property_editor::values::PropertyEditorValues; -use dal::{Component, DalContext, Schema, SchemaVariant}; +use dal::{AttributeValue, Component, DalContext, Schema, SchemaVariant}; use dal_test::test; +use dal_test::test_harness::create_component_for_schema_name; #[test] async fn assemble(ctx: &DalContext) { @@ -32,3 +33,261 @@ async fn assemble(ctx: &DalContext) { .expect("could not assemble property editor schema"); dbg!(property_editor_schema, property_editor_values); } + +#[test] +async fn array_map_manipulation(ctx: &DalContext) { + // Create a component using the testing schema + let component = create_component_for_schema_name(ctx, "pirate", "ss poopcanoe").await; + // let variant_id = Component::schema_variant_id(ctx, component.id()) + // .await + // .expect("find variant id for component"); + + let parrot_names_value_id = component + .attribute_values_for_prop(ctx, &["root", "domain", "parrot_names"]) + .await + .expect("find value ids for the prop parrot_names") + .pop() + .expect("there should only be one value id"); + + let treasure_map_value_id = component + .attribute_values_for_prop(ctx, &["root", "domain", "treasure"]) + .await + .expect("find value ids for the prop treasure") + .pop() + .expect("there should only be one value id"); + + // Add items to array prop + AttributeValue::insert(ctx, parrot_names_value_id, Some("tabitha".into()), None) + .await + .expect("one item in array"); + AttributeValue::insert(ctx, parrot_names_value_id, Some("samantha".into()), None) + .await + .expect("two items in array"); + AttributeValue::insert(ctx, parrot_names_value_id, Some("jessica".into()), None) + .await + .expect("three items in array"); + AttributeValue::insert(ctx, parrot_names_value_id, Some("amanda".into()), None) + .await + .expect("four items in array"); + AttributeValue::insert(ctx, parrot_names_value_id, Some("dr wiggles".into()), None) + .await + .expect("five items in array"); + + // Add items to map prop + AttributeValue::insert( + ctx, + treasure_map_value_id, + Some("cheese".into()), + Some("ohio".to_string()), + ) + .await + .expect("one item in map"); + AttributeValue::insert( + ctx, + treasure_map_value_id, + Some("coxinha".into()), + Some("rio".to_string()), + ) + .await + .expect("two items in map"); + AttributeValue::insert( + ctx, + treasure_map_value_id, + Some("pizza".into()), + Some("nyc".to_string()), + ) + .await + .expect("three items in map"); + AttributeValue::insert( + ctx, + treasure_map_value_id, + Some("sushi".into()), + Some("tokyo".to_string()), + ) + .await + .expect("four items in map"); + AttributeValue::insert( + ctx, + treasure_map_value_id, + Some("baby back ribs".into()), + Some("jupiter".to_string()), + ) + .await + .expect("five items in map"); + + // Grab the children for the array and check that they match what they should be + let parrot_names_child_ids = + AttributeValue::get_child_av_ids_for_ordered_parent(ctx, parrot_names_value_id) + .await + .expect("get the vec of child ids"); + let parrot_names_third_item = AttributeValue::get_by_id( + ctx, + *parrot_names_child_ids + .get(2) + .expect("get the id for the third item"), + ) + .await + .expect("get the third item in the array"); + let parrot_names_third_item_value = parrot_names_third_item + .value(ctx) + .await + .expect("get the value for this array item"); + + // The value of the third item should be "jessica" + assert_eq!(parrot_names_third_item_value, Some("jessica".into())); + + // Grab the children for the map and check that they match what they should be + let treasure_child_ids = + AttributeValue::get_child_av_ids_for_ordered_parent(ctx, treasure_map_value_id) + .await + .expect("get the vec of child ids"); + let treasure_second_item = AttributeValue::get_by_id( + ctx, + *treasure_child_ids + .get(1) + .expect("get the id for the second item"), + ) + .await + .expect("get the second item in the map"); + let treasure_second_item_value = treasure_second_item + .value(ctx) + .await + .expect("get the value for this map item"); + + // The value of the second item should be "coxinha" + assert_eq!(treasure_second_item_value, Some("coxinha".into())); + + let treasure_second_item_key = treasure_second_item + .key(ctx) + .await + .expect("get the key for this map item"); + + // The key of the second item should be "rio" + assert_eq!(treasure_second_item_key, Some("rio".to_string())); + + // Check that there are five items in the array + assert_eq!(parrot_names_child_ids.len(), 5); + + // Check that there are five items in the map + assert_eq!(treasure_child_ids.len(), 5); + + // ====================================================== + // Test removing items from the array + // ====================================================== + + // Remove an item from the array prop + AttributeValue::remove_by_id(ctx, parrot_names_third_item.id()) + .await + .expect("remove the third item in parrot_names array"); + let parrot_names_child_ids = + AttributeValue::get_child_av_ids_for_ordered_parent(ctx, parrot_names_value_id) + .await + .expect("get the vec of child ids"); + + // Check that there are four items in the array + assert_eq!(parrot_names_child_ids.len(), 4); + + // Check that the items around the removed item are correct + + // Get the second item in the array + let parrot_names_second_item = AttributeValue::get_by_id( + ctx, + *parrot_names_child_ids + .get(1) + .expect("get the second item in parrot_names"), + ) + .await + .expect("get the AttributeValue for the second item in parrot_names"); + let parrot_names_second_item_value = parrot_names_second_item + .value(ctx) + .await + .expect("get the value for the second item in parrot_names"); + + // Check that the value of the second array item is "samantha" + assert_eq!(parrot_names_second_item_value, Some("samantha".into())); + + // Get the third item in the array + let parrot_names_third_item = AttributeValue::get_by_id( + ctx, + *parrot_names_child_ids + .get(2) + .expect("get the third item in parrot_names"), + ) + .await + .expect("get the AttributeValue for the third item in parrot_names"); + let parrot_names_third_item_value = parrot_names_third_item + .value(ctx) + .await + .expect("get the value for the third item in parrot_names"); + + // Check that the value of the third array item is "amanda" + assert_eq!(parrot_names_third_item_value, Some("amanda".into())); + + // ====================================================== + // Test removing items from the map + // ====================================================== + + // Remove an item from the map prop + AttributeValue::remove_by_id(ctx, treasure_second_item.id()) + .await + .expect("remove the second item in treasure map"); + let treasure_child_ids = + AttributeValue::get_child_av_ids_for_ordered_parent(ctx, treasure_map_value_id) + .await + .expect("get the vec of child ids"); + + // Check that there are four items in the array + assert_eq!(treasure_child_ids.len(), 4); + + // Check that the items around the removed item are correct + + // Get the first item in the treasure map + let treasure_first_item = AttributeValue::get_by_id( + ctx, + *treasure_child_ids + .get(0) + .expect("get the first item in treasure"), + ) + .await + .expect("get the AttributeValue for the first item in treasure"); + let treasure_first_item_value = treasure_first_item + .value(ctx) + .await + .expect("get the value for the first item in treasure"); + + // Check that the value of the first map item is "cheese" + assert_eq!(treasure_first_item_value, Some("cheese".into())); + + let treasure_first_item_key = treasure_first_item + .key(ctx) + .await + .expect("get the key for the first item in treasure"); + + // Check that the key of the first map item is "ohio" + assert_eq!(treasure_first_item_key, Some("ohio".to_string())); + + // Get the second item in the treasure map + let treasure_second_item = AttributeValue::get_by_id( + ctx, + *treasure_child_ids + .get(1) + .expect("get the second item in treasure"), + ) + .await + .expect("get the AttributeValue for the second item in treasure"); + let treasure_second_item_value = treasure_second_item + .value(ctx) + .await + .expect("get the value for the second item in treasure"); + + // Check that the value of the second map item is "pizza" + assert_eq!(treasure_second_item_value, Some("pizza".into())); + + let treasure_second_item_key = treasure_second_item + .key(ctx) + .await + .expect("get the key for the second item in treasure"); + + // Check that the key of the second map item is "nyc" + assert_eq!(treasure_second_item_key, Some("nyc".to_string())); +} diff --git a/lib/sdf-server/src/server/service/change_set.rs b/lib/sdf-server/src/server/service/change_set.rs index b02a5e714d..eec84c13f9 100644 --- a/lib/sdf-server/src/server/service/change_set.rs +++ b/lib/sdf-server/src/server/service/change_set.rs @@ -16,7 +16,7 @@ use thiserror::Error; use crate::server::state::AppState; -// pub mod abandon_change_set; +pub mod abandon_change_set; // mod abandon_vote; pub mod add_action; pub mod apply_change_set; @@ -134,10 +134,10 @@ pub fn routes() -> Router { "/apply_change_set", post(apply_change_set::apply_change_set), ) - // .route( - // "/abandon_change_set", - // post(abandon_change_set::abandon_change_set), - // ) + .route( + "/abandon_change_set", + post(abandon_change_set::abandon_change_set), + ) // .route( // "/begin_approval_process", // post(begin_approval_process::begin_approval_process), diff --git a/lib/sdf-server/src/server/service/change_set/abandon_change_set.rs b/lib/sdf-server/src/server/service/change_set/abandon_change_set.rs index 6987d55a02..1d54742f47 100644 --- a/lib/sdf-server/src/server/service/change_set/abandon_change_set.rs +++ b/lib/sdf-server/src/server/service/change_set/abandon_change_set.rs @@ -4,7 +4,8 @@ use crate::server::service::change_set::ChangeSetError; use crate::server::tracking::track; use axum::extract::OriginalUri; use axum::Json; -use dal::{ChangeSet, ChangeSetPk, HistoryActor, User, WsEvent}; +use dal::change_set_pointer::ChangeSetPointer; +use dal::{ChangeSetPk, ChangeSetStatus}; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Debug)] @@ -16,7 +17,7 @@ pub struct AbandonChangeSetRequest { #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct AbandonChangeSetResponse { - pub change_set: ChangeSet, + pub change_set: ChangeSetPointer, } pub async fn abandon_change_set( @@ -25,13 +26,17 @@ pub async fn abandon_change_set( PosthogClient(posthog_client): PosthogClient, OriginalUri(original_uri): OriginalUri, Json(request): Json, -) -> ChangeSetResult> { +) -> ChangeSetResult<()> { let mut ctx = builder.build_head(access_builder).await?; - let mut change_set = ChangeSet::get_by_pk(&ctx, &request.change_set_pk) + let mut change_set = ChangeSetPointer::find(&ctx, request.change_set_pk.into()) .await? .ok_or(ChangeSetError::ChangeSetNotFound)?; - change_set.abandon(&mut ctx).await?; + ctx.update_visibility_and_snapshot_to_visibility_no_editing_change_set(&change_set) + .await?; + change_set + .update_status(&ctx, ChangeSetStatus::Abandoned) + .await?; track( &posthog_client, @@ -43,22 +48,20 @@ pub async fn abandon_change_set( }), ); - ctx.blocking_commit().await?; - - let user = match ctx.history_actor() { - HistoryActor::User(user_pk) => User::get_by_pk(&ctx, *user_pk) - .await? - .ok_or(ChangeSetError::InvalidUser(*user_pk))?, + // let user = match ctx.history_actor() { + // HistoryActor::User(user_pk) => User::get_by_pk(&ctx, *user_pk) + // .await? + // .ok_or(ChangeSetError::InvalidUser(*user_pk))?, + // + // HistoryActor::SystemInit => return Err(ChangeSetError::InvalidUserSystemInit), + // }; - HistoryActor::SystemInit => return Err(ChangeSetError::InvalidUserSystemInit), - }; - - WsEvent::change_set_abandoned(&ctx, change_set.pk, Some(user.pk())) - .await? - .publish_on_commit(&ctx) - .await?; + // WsEvent::change_set_abandoned(&ctx, change_set.changeset_pk(), Some(user.pk())) + // .await? + // .publish_on_commit(&ctx) + // .await?; - ctx.commit().await?; + ctx.commit_no_rebase().await?; - Ok(Json(AbandonChangeSetResponse { change_set })) + Ok(()) } diff --git a/lib/sdf-server/src/server/service/component.rs b/lib/sdf-server/src/server/service/component.rs index 9d2f22f1a6..03c4513bf5 100644 --- a/lib/sdf-server/src/server/service/component.rs +++ b/lib/sdf-server/src/server/service/component.rs @@ -20,7 +20,8 @@ pub mod get_property_editor_values; pub mod update_property_editor_value; // pub mod debug; -// pub mod delete_property_editor_value; +pub mod delete_property_editor_value; +// pub mod get_components_metadata; // pub mod get_diff; // pub mod get_resource; pub mod insert_property_editor_value; @@ -168,10 +169,10 @@ pub fn routes() -> Router { "/insert_property_editor_value", post(insert_property_editor_value::insert_property_editor_value), ) - // .route( - // "/delete_property_editor_value", - // post(delete_property_editor_value::delete_property_editor_value), - // ) + .route( + "/delete_property_editor_value", + post(delete_property_editor_value::delete_property_editor_value), + ) .route("/set_type", post(set_type::set_type)) // .route("/refresh", post(refresh::refresh)) // .route("/resource_domain_diff", get(resource_domain_diff::get_diff)) diff --git a/lib/sdf-server/src/server/service/component/delete_property_editor_value.rs b/lib/sdf-server/src/server/service/component/delete_property_editor_value.rs index 90de306ebe..f686ee0230 100644 --- a/lib/sdf-server/src/server/service/component/delete_property_editor_value.rs +++ b/lib/sdf-server/src/server/service/component/delete_property_editor_value.rs @@ -1,11 +1,8 @@ use crate::server::extract::{AccessBuilder, HandlerContext}; -use crate::service::component::{ComponentError, ComponentResult}; +use crate::service::component::ComponentResult; use axum::response::IntoResponse; use axum::Json; -use dal::{ - AttributeReadContext, AttributeValue, AttributeValueId, ChangeSet, ComponentId, Prop, PropId, - PropKind, StandardModel, Visibility, WsEvent, -}; +use dal::{AttributeValue, AttributeValueId, ChangeSet, ComponentId, PropId, Visibility}; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Debug)] @@ -26,74 +23,9 @@ pub async fn delete_property_editor_value( ) -> ComponentResult { let mut ctx = builder.build(request_ctx.build(request.visibility)).await?; - let mut force_changeset_pk = None; - if ctx.visibility().is_head() { - let change_set = ChangeSet::new(&ctx, ChangeSet::generate_name(), None).await?; + let force_changeset_pk = ChangeSet::force_new(&mut ctx).await?; - let new_visibility = Visibility::new(change_set.pk, request.visibility.deleted_at); - - ctx.update_visibility(new_visibility); - - force_changeset_pk = Some(change_set.pk); - - WsEvent::change_set_created(&ctx, change_set.pk) - .await? - .publish_on_commit(&ctx) - .await?; - }; - - let child_prop = Prop::get_by_id(&ctx, &request.prop_id) - .await? - .ok_or(ComponentError::PropNotFound(request.prop_id))?; - - let parent_prop = child_prop - .parent_prop(&ctx) - .await? - .ok_or(ComponentError::RootPropAttributeValue)?; - - let mut av = AttributeValue::get_by_id(&ctx, &request.attribute_value_id) - .await? - .ok_or(ComponentError::AttributeValueNotFound)?; - - let parent_prop_attribute_read_context = AttributeReadContext { - prop_id: Some(*parent_prop.id()), - internal_provider_id: None, - external_provider_id: None, - component_id: Some(request.component_id), - }; - - let mut parent_av = AttributeValue::find_for_context(&ctx, parent_prop_attribute_read_context) - .await? - .ok_or(ComponentError::AttributeValueNotFound)?; - - if *parent_prop.kind() == PropKind::Array { - let array_key = request.key.clone().ok_or(ComponentError::KeyNotFound)?; - - let index_map = parent_av - .index_map_mut() - .map(|index_map| index_map.to_owned()); - - if let Some(mut index_map) = index_map { - let (_, avi_id) = index_map - .order_as_map() - .iter() - .find(|(key, _)| key == &array_key) - .ok_or(ComponentError::AttributeValueNotFound)? - .to_owned(); - - av.delete_by_id(&ctx).await?; - index_map.delete(avi_id); - parent_av.index_map = Some(index_map.to_owned()); - parent_av.update_stored_index_map(&ctx).await?; - } - } - - if *parent_prop.kind() == PropKind::Map { - av.delete_by_id(&ctx).await?; - } - - ctx.enqueue_dependent_values_update(vec![*parent_av.id()]) - .await?; + AttributeValue::remove_by_id(&ctx, request.attribute_value_id).await?; ctx.commit().await?;