Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove View OT/CT #4973

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions lib/dal/src/diagram/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ impl View {

Ok(())
}

pub async fn remove(ctx: &DalContext, view_id: ViewId) -> DiagramResult<()> {
// TODO: Remove the View!!!
Ok(())
}
}

/// Frontend representation for a [View](View).
Expand Down
137 changes: 128 additions & 9 deletions lib/dal/src/workspace_snapshot/node_weight/view_node_weight/v1.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use super::NodeWeightDiscriminants;
use crate::workspace_snapshot::{
content_address::ContentAddress,
graph::LineageId,
node_weight::traits::{CorrectExclusiveOutgoingEdge, CorrectTransforms, SiNodeWeight},
use crate::{
workspace_snapshot::{
content_address::ContentAddress,
graph::{detect_updates::Update, LineageId, WorkspaceSnapshotGraphError},
node_weight::traits::{CorrectExclusiveOutgoingEdge, CorrectTransforms, SiNodeWeight},
EdgeWeightKindDiscriminants, NodeInformation,
},
Timestamp,
};

use crate::{EdgeWeightKindDiscriminants, Timestamp};
use dal_macros::SiNodeWeight;
use jwt_simple::prelude::{Deserialize, Serialize};
use si_events::merkle_tree_hash::MerkleTreeHash;
use si_events::ulid::Ulid;
use si_events::ContentHash;
use petgraph::prelude::*;
use si_events::{merkle_tree_hash::MerkleTreeHash, ulid::Ulid, ContentHash};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, SiNodeWeight)]
#[si_node_weight(discriminant = NodeWeightDiscriminants::View)]
Expand Down Expand Up @@ -39,7 +41,124 @@ impl ViewNodeWeightV1 {
}
}

impl CorrectTransforms for ViewNodeWeightV1 {}
impl CorrectTransforms for ViewNodeWeightV1 {
fn correct_transforms(
&self,
workspace_snapshot_graph: &crate::WorkspaceSnapshotGraphVCurrent,
mut updates: Vec<crate::workspace_snapshot::graph::detect_updates::Update>,
_from_different_change_set: bool,
) -> crate::workspace_snapshot::node_weight::traits::CorrectTransformsResult<
Vec<crate::workspace_snapshot::graph::detect_updates::Update>,
> {
struct GeometryChange {
update_idx: usize,
geometry: NodeInformation,
}

let mut maybe_view_removal_update_idx = None;
let mut removed_geometries = Vec::new();
let mut added_geometries = Vec::new();

for (update_idx, update) in updates.iter().enumerate() {
match update {
Update::RemoveEdge {
source,
destination,
edge_kind,
} if destination.id.into_inner() == self.id.inner()
&& *edge_kind == EdgeWeightKindDiscriminants::Use
&& source.node_weight_kind == NodeWeightDiscriminants::Category =>
{
// This view is being removed.
maybe_view_removal_update_idx = Some(update_idx);
}
Update::RemoveEdge {
source,
destination,
edge_kind,
} if source.id.into_inner() == self.id.inner()
&& *edge_kind == EdgeWeightKindDiscriminants::Use
&& destination.node_weight_kind == NodeWeightDiscriminants::Geometry =>
{
// A Geometry is being removed from this view.
removed_geometries.push(GeometryChange {
update_idx,
geometry: *destination,
});
}
Update::NewEdge {
source,
destination,
edge_weight,
} if source.id.into_inner() == self.id.inner()
&& EdgeWeightKindDiscriminants::from(edge_weight.kind())
== EdgeWeightKindDiscriminants::Use
&& destination.node_weight_kind == NodeWeightDiscriminants::Geometry =>
{
// A Geometry is being added to this view.
added_geometries.push(GeometryChange {
update_idx,
geometry: *destination,
});
}
_ => {}
}
}

if let Some(view_removal_update_idx) = maybe_view_removal_update_idx {
let view_node_index = workspace_snapshot_graph.get_node_index_by_id(self.id())?;

// Make sure that any pre-existing Geometry has a removal in the set of updates.
for (_edge_weight, _source, destination) in workspace_snapshot_graph
.edges_directed_for_edge_weight_kind(
view_node_index,
Direction::Outgoing,
EdgeWeightKindDiscriminants::Use,
)
{
let existing_geometry_id =
workspace_snapshot_graph
.node_index_to_id(destination)
.ok_or_else(|| WorkspaceSnapshotGraphError::NodeWeightNotFound)?;
// Most of the time, the set of Geometry removal updates should be <= the set of
// pre-existing Geometries, since if the view is being removed, we'll want to also
// remove all Geometries from the view (=), but there may have also been new
// Geometries added that we didn't know about when the Updates were calculated (<).
//
// We want the one most likely to have the smaller cardinality to be the one we
// loop over in the inner loop to try to minimize the number of iterations.
if !removed_geometries.iter().any(|geometry_removal| {
existing_geometry_id == geometry_removal.geometry.id.into()
}) {
updates.remove(view_removal_update_idx);

return Ok(updates);
}
}

// There really shouldn't ever be any Geometry additions in the same set of Updates
// where we're also removing the View the Geometries were added to, but just in case we
// somehow are adding Geometries that aren't later removed in the set of Updates (also
// something that really _shouldn't_ happen), we'll want to remove the Update that
// removes the View, and leave all other Updates in place.
for geometry_addition in added_geometries {
// See if there is also a removal for the same Geometry, and make sure that the
// addition happens _before_ the removal in the list of Updates to ensure it's a
// net-removal (non-addition?) of the Geometry.
if !removed_geometries.iter().any(|geometry_removal| {
geometry_addition.geometry.id == geometry_removal.geometry.id
&& geometry_addition.update_idx < geometry_removal.update_idx
}) {
updates.remove(view_removal_update_idx);

return Ok(updates);
}
}
}

Ok(updates)
}
}

impl CorrectExclusiveOutgoingEdge for ViewNodeWeightV1 {
fn exclusive_outgoing_edges(&self) -> &[EdgeWeightKindDiscriminants] {
Expand Down
2 changes: 2 additions & 0 deletions lib/dal/tests/integration_test/node_weight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ mod attribute_value;
mod component;
mod ordering;
mod schema_variant;
mod view;

54 changes: 54 additions & 0 deletions lib/dal/tests/integration_test/node_weight/view.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use dal::{
diagram::{geometry::Geometry, view::View},
Component, DalContext,
};
use dal_test::{
expected::{self, ExpectComponent, ExpectView},
test,
};
use pretty_assertions_sorted::assert_eq;

#[test]
async fn correct_transforms_view_removed_all_geometries_removed(ctx: &mut DalContext) {
let component = ExpectComponent::create(ctx, "Docker Image").await;
let new_view = ExpectView::create(ctx).await;
let _component_geometry = Geometry::new(ctx, component.id(), new_view.id())
.await
.expect("Unable to create Geometry for Component in View");

expected::apply_change_set_to_base(ctx).await;
expected::fork_from_head_change_set(ctx).await;

let views = View::list(ctx).await.expect("Unable to list Views");
assert_eq!(2, views.len());

assert_eq!(
1,
Geometry::list_by_view_id(ctx, new_view.id())
.await
.expect("Unable to list Geometries for View")
.len(),
);

Component::remove(ctx, component.id())
.await
.expect("Unable to remove Component");
}

#[test]
async fn correct_transforms_view_removed_not_all_geometries_removed(ctx: &mut DalContext) {
let component = ExpectComponent::create(ctx, "Docker Image").await;
let new_view = ExpectView::create(ctx).await;

let views = View::list(ctx).await.expect("Unable to list Views");
assert_eq!(2, views.len());
}

#[test]
async fn correct_transforms_view_removed_no_other_views(ctx: &mut DalContext) {
let default_view = ExpectView::get_id_for_default(ctx).await;
let new_view = ExpectView::create(ctx).await;

let views = View::list(ctx).await.expect("Unable to list Views");
assert_eq!(2, views.len());
}